diff options
Diffstat (limited to 'bcprov')
474 files changed, 30210 insertions, 6146 deletions
diff --git a/bcprov/src/main/java/org/bouncycastle/LICENSE.java b/bcprov/src/main/java/org/bouncycastle/LICENSE.java index b97d88b..b02dfbc 100644 --- a/bcprov/src/main/java/org/bouncycastle/LICENSE.java +++ b/bcprov/src/main/java/org/bouncycastle/LICENSE.java @@ -3,7 +3,7 @@ package org.bouncycastle; /** * The Bouncy Castle License * - * Copyright (c) 2000-2012 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) + * Copyright (c) 2000-2013 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) * <p> * 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, @@ -24,7 +24,7 @@ package org.bouncycastle; public class LICENSE { public static String licenseText = - "Copyright (c) 2000-2012 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) " + "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 " diff --git a/bcprov/src/main/java/org/bouncycastle/apache/bzip2/BZip2Constants.java b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/BZip2Constants.java new file mode 100644 index 0000000..e86bdee --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/BZip2Constants.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * <keiron@aftexsw.com> to whom the Ant project is very grateful for his + * great code. + */ + +package org.bouncycastle.apache.bzip2; + +/** + * Base class for both the compress and decompress classes. + * Holds common arrays, and static data. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + */ +public interface BZip2Constants { + + int baseBlockSize = 100000; + int MAX_ALPHA_SIZE = 258; + int MAX_CODE_LEN = 23; + int RUNA = 0; + int RUNB = 1; + int N_GROUPS = 6; + int G_SIZE = 50; + int N_ITERS = 4; + int MAX_SELECTORS = (2 + (900000 / G_SIZE)); + int NUM_OVERSHOOT_BYTES = 20; + + int[] rNums = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 + }; +} diff --git a/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2InputStream.java b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2InputStream.java new file mode 100644 index 0000000..08d05e7 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2InputStream.java @@ -0,0 +1,848 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * <keiron@aftexsw.com> to whom the Ant project is very grateful for his + * great code. + */ +package org.bouncycastle.apache.bzip2; + +import java.io.InputStream; +import java.io.IOException; + +/** + * An input stream that decompresses from the BZip2 format (with the file + * header chars) to be read as any other stream. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + * + * <b>NB:</b> note this class has been modified to read the leading BZ from the + * start of the BZIP2 stream to make it compatible with other PGP programs. + */ +public class CBZip2InputStream extends InputStream implements BZip2Constants { + private static void cadvise() { + System.out.println("CRC Error"); + //throw new CCoruptionError(); + } + +// private static void badBGLengths() { +// cadvise(); +// } +// +// private static void bitStreamEOF() { +// cadvise(); +// } + + private static void compressedStreamEOF() { + cadvise(); + } + + private void makeMaps() { + int i; + nInUse = 0; + for (i = 0; i < 256; i++) { + if (inUse[i]) { + seqToUnseq[nInUse] = (char) i; + unseqToSeq[i] = (char) nInUse; + nInUse++; + } + } + } + + /* + index of the last char in the block, so + the block size == last + 1. + */ + private int last; + + /* + index in zptr[] of original string after sorting. + */ + private int origPtr; + + /* + always: in the range 0 .. 9. + The current block size is 100000 * this number. + */ + private int blockSize100k; + + private boolean blockRandomised; + + private int bsBuff; + private int bsLive; + private CRC mCrc = new CRC(); + + private boolean[] inUse = new boolean[256]; + private int nInUse; + + private char[] seqToUnseq = new char[256]; + private char[] unseqToSeq = new char[256]; + + private char[] selector = new char[MAX_SELECTORS]; + private char[] selectorMtf = new char[MAX_SELECTORS]; + + private int[] tt; + private char[] ll8; + + /* + freq table collected to save a pass over the data + during decompression. + */ + private int[] unzftab = new int[256]; + + private int[][] limit = new int[N_GROUPS][MAX_ALPHA_SIZE]; + private int[][] base = new int[N_GROUPS][MAX_ALPHA_SIZE]; + private int[][] perm = new int[N_GROUPS][MAX_ALPHA_SIZE]; + private int[] minLens = new int[N_GROUPS]; + + private InputStream bsStream; + + private boolean streamEnd = false; + + private int currentChar = -1; + + private static final int START_BLOCK_STATE = 1; + private static final int RAND_PART_A_STATE = 2; + private static final int RAND_PART_B_STATE = 3; + private static final int RAND_PART_C_STATE = 4; + private static final int NO_RAND_PART_A_STATE = 5; + private static final int NO_RAND_PART_B_STATE = 6; + private static final int NO_RAND_PART_C_STATE = 7; + + private int currentState = START_BLOCK_STATE; + + private int storedBlockCRC, storedCombinedCRC; + private int computedBlockCRC, computedCombinedCRC; + + int i2, count, chPrev, ch2; + int i, tPos; + int rNToGo = 0; + int rTPos = 0; + int j2; + char z; + + public CBZip2InputStream(InputStream zStream) + throws IOException + { + ll8 = null; + tt = null; + bsSetStream(zStream); + initialize(); + initBlock(); + setupBlock(); + } + + public int read() { + if (streamEnd) { + return -1; + } else { + int retChar = currentChar; + switch(currentState) { + case START_BLOCK_STATE: + break; + case RAND_PART_A_STATE: + break; + case RAND_PART_B_STATE: + setupRandPartB(); + break; + case RAND_PART_C_STATE: + setupRandPartC(); + break; + case NO_RAND_PART_A_STATE: + break; + case NO_RAND_PART_B_STATE: + setupNoRandPartB(); + break; + case NO_RAND_PART_C_STATE: + setupNoRandPartC(); + break; + default: + break; + } + return retChar; + } + } + + private void initialize() throws IOException { + char magic3, magic4; + magic3 = bsGetUChar(); + magic4 = bsGetUChar(); + if (magic3 != 'B' && magic4 != 'Z') + { + throw new IOException("Not a BZIP2 marked stream"); + } + magic3 = bsGetUChar(); + magic4 = bsGetUChar(); + if (magic3 != 'h' || magic4 < '1' || magic4 > '9') { + bsFinishedWithStream(); + streamEnd = true; + return; + } + + setDecompressStructureSizes(magic4 - '0'); + computedCombinedCRC = 0; + } + + private void initBlock() { + char magic1, magic2, magic3, magic4; + char magic5, magic6; + magic1 = bsGetUChar(); + magic2 = bsGetUChar(); + magic3 = bsGetUChar(); + magic4 = bsGetUChar(); + magic5 = bsGetUChar(); + magic6 = bsGetUChar(); + if (magic1 == 0x17 && magic2 == 0x72 && magic3 == 0x45 + && magic4 == 0x38 && magic5 == 0x50 && magic6 == 0x90) { + complete(); + return; + } + + if (magic1 != 0x31 || magic2 != 0x41 || magic3 != 0x59 + || magic4 != 0x26 || magic5 != 0x53 || magic6 != 0x59) { + badBlockHeader(); + streamEnd = true; + return; + } + + storedBlockCRC = bsGetInt32(); + + if (bsR(1) == 1) { + blockRandomised = true; + } else { + blockRandomised = false; + } + + // currBlockNo++; + getAndMoveToFrontDecode(); + + mCrc.initialiseCRC(); + currentState = START_BLOCK_STATE; + } + + private void endBlock() { + computedBlockCRC = mCrc.getFinalCRC(); + /* A bad CRC is considered a fatal error. */ + if (storedBlockCRC != computedBlockCRC) { + crcError(); + } + + computedCombinedCRC = (computedCombinedCRC << 1) + | (computedCombinedCRC >>> 31); + computedCombinedCRC ^= computedBlockCRC; + } + + private void complete() { + storedCombinedCRC = bsGetInt32(); + if (storedCombinedCRC != computedCombinedCRC) { + crcError(); + } + + bsFinishedWithStream(); + streamEnd = true; + } + + private static void blockOverrun() { + cadvise(); + } + + private static void badBlockHeader() { + cadvise(); + } + + private static void crcError() { + cadvise(); + } + + private void bsFinishedWithStream() { + try { + if (this.bsStream != null) { + if (this.bsStream != System.in) { + this.bsStream.close(); + this.bsStream = null; + } + } + } catch (IOException ioe) { + //ignore + } + } + + private void bsSetStream(InputStream f) { + bsStream = f; + bsLive = 0; + bsBuff = 0; + } + + private int bsR(int n) { + int v; + while (bsLive < n) { + int zzi; + char thech = 0; + try { + thech = (char) bsStream.read(); + } catch (IOException e) { + compressedStreamEOF(); + } + if (thech == -1) { + compressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + + v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1); + bsLive -= n; + return v; + } + + private char bsGetUChar() { + return (char) bsR(8); + } + + private int bsGetint() { + int u = 0; + u = (u << 8) | bsR(8); + u = (u << 8) | bsR(8); + u = (u << 8) | bsR(8); + u = (u << 8) | bsR(8); + return u; + } + + private int bsGetIntVS(int numBits) { + return (int) bsR(numBits); + } + + private int bsGetInt32() { + return (int) bsGetint(); + } + + private void hbCreateDecodeTables(int[] limit, int[] base, + int[] perm, char[] length, + int minLen, int maxLen, int alphaSize) { + int pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) { + for (j = 0; j < alphaSize; j++) { + if (length[j] == i) { + perm[pp] = j; + pp++; + } + } + } + + for (i = 0; i < MAX_CODE_LEN; i++) { + base[i] = 0; + } + for (i = 0; i < alphaSize; i++) { + base[length[i] + 1]++; + } + + for (i = 1; i < MAX_CODE_LEN; i++) { + base[i] += base[i - 1]; + } + + for (i = 0; i < MAX_CODE_LEN; i++) { + limit[i] = 0; + } + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (base[i + 1] - base[i]); + limit[i] = vec - 1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) { + base[i] = ((limit[i - 1] + 1) << 1) - base[i]; + } + } + + private void recvDecodingTables() { + char len[][] = new char[N_GROUPS][MAX_ALPHA_SIZE]; + int i, j, t, nGroups, nSelectors, alphaSize; + int minLen, maxLen; + boolean[] inUse16 = new boolean[16]; + + /* Receive the mapping table */ + for (i = 0; i < 16; i++) { + if (bsR(1) == 1) { + inUse16[i] = true; + } else { + inUse16[i] = false; + } + } + + for (i = 0; i < 256; i++) { + inUse[i] = false; + } + + for (i = 0; i < 16; i++) { + if (inUse16[i]) { + for (j = 0; j < 16; j++) { + if (bsR(1) == 1) { + inUse[i * 16 + j] = true; + } + } + } + } + + makeMaps(); + alphaSize = nInUse + 2; + + /* Now the selectors */ + nGroups = bsR(3); + nSelectors = bsR(15); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (bsR(1) == 1) { + j++; + } + selectorMtf[i] = (char) j; + } + + /* Undo the MTF values for the selectors. */ + { + char[] pos = new char[N_GROUPS]; + char tmp, v; + for (v = 0; v < nGroups; v++) { + pos[v] = v; + } + + for (i = 0; i < nSelectors; i++) { + v = selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { + pos[v] = pos[v - 1]; + v--; + } + pos[0] = tmp; + selector[i] = tmp; + } + } + + /* Now the coding tables */ + for (t = 0; t < nGroups; t++) { + int curr = bsR(5); + for (i = 0; i < alphaSize; i++) { + while (bsR(1) == 1) { + if (bsR(1) == 0) { + curr++; + } else { + curr--; + } + } + len[t][i] = (char) curr; + } + } + + /* Create the Huffman decoding tables */ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (len[t][i] > maxLen) { + maxLen = len[t][i]; + } + if (len[t][i] < minLen) { + minLen = len[t][i]; + } + } + hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen, + maxLen, alphaSize); + minLens[t] = minLen; + } + } + + private void getAndMoveToFrontDecode() { + char[] yy = new char[256]; + int i, j, nextSym, limitLast; + int EOB, groupNo, groupPos; + + limitLast = baseBlockSize * blockSize100k; + origPtr = bsGetIntVS(24); + + recvDecodingTables(); + EOB = nInUse + 1; + groupNo = -1; + groupPos = 0; + + /* + Setting up the unzftab entries here is not strictly + necessary, but it does save having to do it later + in a separate pass, and so saves a block's worth of + cache misses. + */ + for (i = 0; i <= 255; i++) { + unzftab[i] = 0; + } + + for (i = 0; i <= 255; i++) { + yy[i] = (char) i; + } + + last = -1; + + { + int zt, zn, zvec, zj; + if (groupPos == 0) { + groupNo++; + groupPos = G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = bsR(zn); + while (zvec > limit[zt][zn]) { + zn++; + { + { + while (bsLive < 1) { + int zzi; + char thech = 0; + try { + thech = (char) bsStream.read(); + } catch (IOException e) { + compressedStreamEOF(); + } + if (thech == -1) { + compressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - base[zt][zn]]; + } + + while (true) { + + if (nextSym == EOB) { + break; + } + + if (nextSym == RUNA || nextSym == RUNB) { + char ch; + int s = -1; + int N = 1; + do { + if (nextSym == RUNA) { + s = s + (0 + 1) * N; + } else if (nextSym == RUNB) { + s = s + (1 + 1) * N; + } + N = N * 2; + { + int zt, zn, zvec, zj; + if (groupPos == 0) { + groupNo++; + groupPos = G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = bsR(zn); + while (zvec > limit[zt][zn]) { + zn++; + { + { + while (bsLive < 1) { + int zzi; + char thech = 0; + try { + thech = (char) bsStream.read(); + } catch (IOException e) { + compressedStreamEOF(); + } + if (thech == -1) { + compressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - base[zt][zn]]; + } + } while (nextSym == RUNA || nextSym == RUNB); + + s++; + ch = seqToUnseq[yy[0]]; + unzftab[ch] += s; + + while (s > 0) { + last++; + ll8[last] = ch; + s--; + } + + if (last >= limitLast) { + blockOverrun(); + } + continue; + } else { + char tmp; + last++; + if (last >= limitLast) { + blockOverrun(); + } + + tmp = yy[nextSym - 1]; + unzftab[seqToUnseq[tmp]]++; + ll8[last] = seqToUnseq[tmp]; + + /* + This loop is hammered during decompression, + hence the unrolling. + + for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1]; + */ + + j = nextSym - 1; + for (; j > 3; j -= 4) { + yy[j] = yy[j - 1]; + yy[j - 1] = yy[j - 2]; + yy[j - 2] = yy[j - 3]; + yy[j - 3] = yy[j - 4]; + } + for (; j > 0; j--) { + yy[j] = yy[j - 1]; + } + + yy[0] = tmp; + { + int zt, zn, zvec, zj; + if (groupPos == 0) { + groupNo++; + groupPos = G_SIZE; + } + groupPos--; + zt = selector[groupNo]; + zn = minLens[zt]; + zvec = bsR(zn); + while (zvec > limit[zt][zn]) { + zn++; + { + { + while (bsLive < 1) { + int zzi; + char thech = 0; + try { + thech = (char) bsStream.read(); + } catch (IOException e) { + compressedStreamEOF(); + } + zzi = thech; + bsBuff = (bsBuff << 8) | (zzi & 0xff); + bsLive += 8; + } + } + zj = (bsBuff >> (bsLive - 1)) & 1; + bsLive--; + } + zvec = (zvec << 1) | zj; + } + nextSym = perm[zt][zvec - base[zt][zn]]; + } + continue; + } + } + } + + private void setupBlock() { + int[] cftab = new int[257]; + char ch; + + cftab[0] = 0; + for (i = 1; i <= 256; i++) { + cftab[i] = unzftab[i - 1]; + } + for (i = 1; i <= 256; i++) { + cftab[i] += cftab[i - 1]; + } + + for (i = 0; i <= last; i++) { + ch = (char) ll8[i]; + tt[cftab[ch]] = i; + cftab[ch]++; + } + cftab = null; + + tPos = tt[origPtr]; + + count = 0; + i2 = 0; + ch2 = 256; /* not a char and not EOF */ + + if (blockRandomised) { + rNToGo = 0; + rTPos = 0; + setupRandPartA(); + } else { + setupNoRandPartA(); + } + } + + private void setupRandPartA() { + if (i2 <= last) { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + if (rNToGo == 0) { + rNToGo = rNums[rTPos]; + rTPos++; + if (rTPos == 512) { + rTPos = 0; + } + } + rNToGo--; + ch2 ^= (int) ((rNToGo == 1) ? 1 : 0); + i2++; + + currentChar = ch2; + currentState = RAND_PART_B_STATE; + mCrc.updateCRC(ch2); + } else { + endBlock(); + initBlock(); + setupBlock(); + } + } + + private void setupNoRandPartA() { + if (i2 <= last) { + chPrev = ch2; + ch2 = ll8[tPos]; + tPos = tt[tPos]; + i2++; + + currentChar = ch2; + currentState = NO_RAND_PART_B_STATE; + mCrc.updateCRC(ch2); + } else { + endBlock(); + initBlock(); + setupBlock(); + } + } + + private void setupRandPartB() { + if (ch2 != chPrev) { + currentState = RAND_PART_A_STATE; + count = 1; + setupRandPartA(); + } else { + count++; + if (count >= 4) { + z = ll8[tPos]; + tPos = tt[tPos]; + if (rNToGo == 0) { + rNToGo = rNums[rTPos]; + rTPos++; + if (rTPos == 512) { + rTPos = 0; + } + } + rNToGo--; + z ^= ((rNToGo == 1) ? 1 : 0); + j2 = 0; + currentState = RAND_PART_C_STATE; + setupRandPartC(); + } else { + currentState = RAND_PART_A_STATE; + setupRandPartA(); + } + } + } + + private void setupRandPartC() { + if (j2 < (int) z) { + currentChar = ch2; + mCrc.updateCRC(ch2); + j2++; + } else { + currentState = RAND_PART_A_STATE; + i2++; + count = 0; + setupRandPartA(); + } + } + + private void setupNoRandPartB() { + if (ch2 != chPrev) { + currentState = NO_RAND_PART_A_STATE; + count = 1; + setupNoRandPartA(); + } else { + count++; + if (count >= 4) { + z = ll8[tPos]; + tPos = tt[tPos]; + currentState = NO_RAND_PART_C_STATE; + j2 = 0; + setupNoRandPartC(); + } else { + currentState = NO_RAND_PART_A_STATE; + setupNoRandPartA(); + } + } + } + + private void setupNoRandPartC() { + if (j2 < (int) z) { + currentChar = ch2; + mCrc.updateCRC(ch2); + j2++; + } else { + currentState = NO_RAND_PART_A_STATE; + i2++; + count = 0; + setupNoRandPartA(); + } + } + + private void setDecompressStructureSizes(int newSize100k) { + if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k + && blockSize100k <= 9)) { + // throw new IOException("Invalid block size"); + } + + blockSize100k = newSize100k; + + if (newSize100k == 0) { + return; + } + + int n = baseBlockSize * newSize100k; + ll8 = new char[n]; + tt = new int[n]; + } +} + diff --git a/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2OutputStream.java b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2OutputStream.java new file mode 100644 index 0000000..0503583 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2OutputStream.java @@ -0,0 +1,1651 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * <keiron@aftexsw.com> to whom the Ant project is very grateful for his + * great code. + */ + +package org.bouncycastle.apache.bzip2; + +import java.io.OutputStream; +import java.io.IOException; + +/** + * An output stream that compresses into the BZip2 format (with the file + * header chars) into another stream. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + * + * TODO: Update to BZip2 1.0.1 + * <b>NB:</b> note this class has been modified to add a leading BZ to the + * start of the BZIP2 stream to make it compatible with other PGP programs. + */ +public class CBZip2OutputStream extends OutputStream implements BZip2Constants { + protected static final int SETMASK = (1 << 21); + protected static final int CLEARMASK = (~SETMASK); + protected static final int GREATER_ICOST = 15; + protected static final int LESSER_ICOST = 0; + protected static final int SMALL_THRESH = 20; + protected static final int DEPTH_THRESH = 10; + + /* + If you are ever unlucky/improbable enough + to get a stack overflow whilst sorting, + increase the following constant and try + again. In practice I have never seen the + stack go above 27 elems, so the following + limit seems very generous. + */ + protected static final int QSORT_STACK_SIZE = 1000; + private boolean finished; + + private static void panic() { + System.out.println("panic"); + //throw new CError(); + } + + private void makeMaps() { + int i; + nInUse = 0; + for (i = 0; i < 256; i++) { + if (inUse[i]) { + seqToUnseq[nInUse] = (char) i; + unseqToSeq[i] = (char) nInUse; + nInUse++; + } + } + } + + protected static void hbMakeCodeLengths(char[] len, int[] freq, + int alphaSize, int maxLen) { + /* + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + */ + int nNodes, nHeap, n1, n2, i, j, k; + boolean tooLong; + + int[] heap = new int[MAX_ALPHA_SIZE + 2]; + int[] weight = new int[MAX_ALPHA_SIZE * 2]; + int[] parent = new int[MAX_ALPHA_SIZE * 2]; + + for (i = 0; i < alphaSize; i++) { + weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + } + + while (true) { + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + { + int zz, tmp; + zz = nHeap; + tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + } + } + if (!(nHeap < (MAX_ALPHA_SIZE + 2))) { + panic(); + } + + while (nHeap > 1) { + n1 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + { + int zz = 0, yy = 0, tmp = 0; + zz = 1; + tmp = heap[zz]; + while (true) { + yy = zz << 1; + if (yy > nHeap) { + break; + } + if (yy < nHeap + && weight[heap[yy + 1]] < weight[heap[yy]]) { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) { + break; + } + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; + } + n2 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + { + int zz = 0, yy = 0, tmp = 0; + zz = 1; + tmp = heap[zz]; + while (true) { + yy = zz << 1; + if (yy > nHeap) { + break; + } + if (yy < nHeap + && weight[heap[yy + 1]] < weight[heap[yy]]) { + yy++; + } + if (weight[tmp] < weight[heap[yy]]) { + break; + } + heap[zz] = heap[yy]; + zz = yy; + } + heap[zz] = tmp; + } + nNodes++; + parent[n1] = parent[n2] = nNodes; + + weight[nNodes] = ((weight[n1] & 0xffffff00) + + (weight[n2] & 0xffffff00)) + | (1 + (((weight[n1] & 0x000000ff) > + (weight[n2] & 0x000000ff)) ? + (weight[n1] & 0x000000ff) : + (weight[n2] & 0x000000ff))); + + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + { + int zz = 0, tmp = 0; + zz = nHeap; + tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + } + } + if (!(nNodes < (MAX_ALPHA_SIZE * 2))) { + panic(); + } + + tooLong = false; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { + k = parent[k]; + j++; + } + len[i - 1] = (char) j; + if (j > maxLen) { + tooLong = true; + } + } + + if (!tooLong) { + break; + } + + for (i = 1; i < alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } + } + + /* + index of the last char in the block, so + the block size == last + 1. + */ + int last; + + /* + index in zptr[] of original string after sorting. + */ + int origPtr; + + /* + always: in the range 0 .. 9. + The current block size is 100000 * this number. + */ + int blockSize100k; + + boolean blockRandomised; + + int bytesOut; + int bsBuff; + int bsLive; + CRC mCrc = new CRC(); + + private boolean[] inUse = new boolean[256]; + private int nInUse; + + private char[] seqToUnseq = new char[256]; + private char[] unseqToSeq = new char[256]; + + private char[] selector = new char[MAX_SELECTORS]; + private char[] selectorMtf = new char[MAX_SELECTORS]; + + private char[] block; + private int[] quadrant; + private int[] zptr; + private short[] szptr; + private int[] ftab; + + private int nMTF; + + private int[] mtfFreq = new int[MAX_ALPHA_SIZE]; + + /* + * Used when sorting. If too many long comparisons + * happen, we stop sorting, randomise the block + * slightly, and try again. + */ + private int workFactor; + private int workDone; + private int workLimit; + private boolean firstAttempt; + private int nBlocksRandomised; + + private int currentChar = -1; + private int runLength = 0; + + public CBZip2OutputStream(OutputStream inStream) throws IOException { + this(inStream, 9); + } + + public CBZip2OutputStream(OutputStream inStream, int inBlockSize) + throws IOException { + block = null; + quadrant = null; + zptr = null; + ftab = null; + + inStream.write('B'); + inStream.write('Z'); + + bsSetStream(inStream); + + workFactor = 50; + if (inBlockSize > 9) { + inBlockSize = 9; + } + if (inBlockSize < 1) { + inBlockSize = 1; + } + blockSize100k = inBlockSize; + allocateCompressStructures(); + initialize(); + initBlock(); + } + + /** + * + * modified by Oliver Merkel, 010128 + * + */ + public void write(int bv) throws IOException { + int b = (256 + bv) % 256; + if (currentChar != -1) { + if (currentChar == b) { + runLength++; + if (runLength > 254) { + writeRun(); + currentChar = -1; + runLength = 0; + } + } else { + writeRun(); + runLength = 1; + currentChar = b; + } + } else { + currentChar = b; + runLength++; + } + } + + private void writeRun() throws IOException { + if (last < allowableBlockSize) { + inUse[currentChar] = true; + for (int i = 0; i < runLength; i++) { + mCrc.updateCRC((char) currentChar); + } + switch (runLength) { + case 1: + last++; + block[last + 1] = (char) currentChar; + break; + case 2: + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + break; + case 3: + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + break; + default: + inUse[runLength - 4] = true; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) currentChar; + last++; + block[last + 1] = (char) (runLength - 4); + break; + } + } else { + endBlock(); + initBlock(); + writeRun(); + } + } + + boolean closed = false; + + protected void finalize() throws Throwable { + close(); + super.finalize(); + } + + public void close() throws IOException { + if (closed) { + return; + } + + finish(); + + closed = true; + super.close(); + bsStream.close(); + } + + public void finish() throws IOException { + if (finished) { + return; + } + + if (runLength > 0) { + writeRun(); + } + currentChar = -1; + endBlock(); + endCompression(); + finished = true; + flush(); + } + + public void flush() throws IOException { + super.flush(); + bsStream.flush(); + } + + private int blockCRC, combinedCRC; + + private void initialize() throws IOException { + bytesOut = 0; + nBlocksRandomised = 0; + + /* Write `magic' bytes h indicating file-format == huffmanised, + followed by a digit indicating blockSize100k. + */ + bsPutUChar('h'); + bsPutUChar('0' + blockSize100k); + + combinedCRC = 0; + } + + private int allowableBlockSize; + + private void initBlock() { + // blockNo++; + mCrc.initialiseCRC(); + last = -1; + // ch = 0; + + for (int i = 0; i < 256; i++) { + inUse[i] = false; + } + + /* 20 is just a paranoia constant */ + allowableBlockSize = baseBlockSize * blockSize100k - 20; + } + + private void endBlock() throws IOException { + blockCRC = mCrc.getFinalCRC(); + combinedCRC = (combinedCRC << 1) | (combinedCRC >>> 31); + combinedCRC ^= blockCRC; + + /* sort the block and establish posn of original string */ + doReversibleTransformation(); + + /* + A 6-byte block header, the value chosen arbitrarily + as 0x314159265359 :-). A 32 bit value does not really + give a strong enough guarantee that the value will not + appear by chance in the compressed datastream. Worst-case + probability of this event, for a 900k block, is about + 2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 bits. + For a compressed file of size 100Gb -- about 100000 blocks -- + only a 48-bit marker will do. NB: normal compression/ + decompression do *not* rely on these statistical properties. + They are only important when trying to recover blocks from + damaged files. + */ + bsPutUChar(0x31); + bsPutUChar(0x41); + bsPutUChar(0x59); + bsPutUChar(0x26); + bsPutUChar(0x53); + bsPutUChar(0x59); + + /* Now the block's CRC, so it is in a known place. */ + bsPutint(blockCRC); + + /* Now a single bit indicating randomisation. */ + if (blockRandomised) { + bsW(1, 1); + nBlocksRandomised++; + } else { + bsW(1, 0); + } + + /* Finally, block's contents proper. */ + moveToFrontCodeAndSend(); + } + + private void endCompression() throws IOException { + /* + Now another magic 48-bit number, 0x177245385090, to + indicate the end of the last block. (sqrt(pi), if + you want to know. I did want to use e, but it contains + too much repetition -- 27 18 28 18 28 46 -- for me + to feel statistically comfortable. Call me paranoid.) + */ + bsPutUChar(0x17); + bsPutUChar(0x72); + bsPutUChar(0x45); + bsPutUChar(0x38); + bsPutUChar(0x50); + bsPutUChar(0x90); + + bsPutint(combinedCRC); + + bsFinishedWithStream(); + } + + private void hbAssignCodes (int[] code, char[] length, int minLen, + int maxLen, int alphaSize) { + int n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) { + if (length[i] == n) { + code[i] = vec; + vec++; + } + } + vec <<= 1; + } + } + + private void bsSetStream(OutputStream f) { + bsStream = f; + bsLive = 0; + bsBuff = 0; + bytesOut = 0; + } + + private void bsFinishedWithStream() throws IOException { + while (bsLive > 0) { + int ch = (bsBuff >> 24); + try { + bsStream.write(ch); // write 8-bit + } catch (IOException e) { + throw e; + } + bsBuff <<= 8; + bsLive -= 8; + bytesOut++; + } + } + + private void bsW(int n, int v) throws IOException { + while (bsLive >= 8) { + int ch = (bsBuff >> 24); + try { + bsStream.write(ch); // write 8-bit + } catch (IOException e) { + throw e; + } + bsBuff <<= 8; + bsLive -= 8; + bytesOut++; + } + bsBuff |= (v << (32 - bsLive - n)); + bsLive += n; + } + + private void bsPutUChar(int c) throws IOException { + bsW(8, c); + } + + private void bsPutint(int u) throws IOException { + bsW(8, (u >> 24) & 0xff); + bsW(8, (u >> 16) & 0xff); + bsW(8, (u >> 8) & 0xff); + bsW(8, u & 0xff); + } + + private void bsPutIntVS(int numBits, int c) throws IOException { + bsW(numBits, c); + } + + private void sendMTFValues() throws IOException { + char len[][] = new char[N_GROUPS][MAX_ALPHA_SIZE]; + + int v, t, i, j, gs, ge, totc, bt, bc, iter; + int nSelectors = 0, alphaSize, minLen, maxLen, selCtr; + int nGroups;//, nBytes; + + alphaSize = nInUse + 2; + for (t = 0; t < N_GROUPS; t++) { + for (v = 0; v < alphaSize; v++) { + len[t][v] = (char) GREATER_ICOST; + } + } + + /* Decide how many coding tables to use */ + if (nMTF <= 0) { + panic(); + } + + if (nMTF < 200) { + nGroups = 2; + } else if (nMTF < 600) { + nGroups = 3; + } else if (nMTF < 1200) { + nGroups = 4; + } else if (nMTF < 2400) { + nGroups = 5; + } else { + nGroups = 6; + } + + /* Generate an initial set of coding tables */ { + int nPart, remF, tFreq, aFreq; + + nPart = nGroups; + remF = nMTF; + gs = 0; + while (nPart > 0) { + tFreq = remF / nPart; + ge = gs - 1; + aFreq = 0; + while (aFreq < tFreq && ge < alphaSize - 1) { + ge++; + aFreq += mtfFreq[ge]; + } + + if (ge > gs && nPart != nGroups && nPart != 1 + && ((nGroups - nPart) % 2 == 1)) { + aFreq -= mtfFreq[ge]; + ge--; + } + + for (v = 0; v < alphaSize; v++) { + if (v >= gs && v <= ge) { + len[nPart - 1][v] = (char) LESSER_ICOST; + } else { + len[nPart - 1][v] = (char) GREATER_ICOST; + } + } + + nPart--; + gs = ge + 1; + remF -= aFreq; + } + } + + int[][] rfreq = new int[N_GROUPS][MAX_ALPHA_SIZE]; + int[] fave = new int[N_GROUPS]; + short[] cost = new short[N_GROUPS]; + /* + Iterate up to N_ITERS times to improve the tables. + */ + for (iter = 0; iter < N_ITERS; iter++) { + for (t = 0; t < nGroups; t++) { + fave[t] = 0; + } + + for (t = 0; t < nGroups; t++) { + for (v = 0; v < alphaSize; v++) { + rfreq[t][v] = 0; + } + } + + nSelectors = 0; + totc = 0; + gs = 0; + while (true) { + + /* Set group start & end marks. */ + if (gs >= nMTF) { + break; + } + ge = gs + G_SIZE - 1; + if (ge >= nMTF) { + ge = nMTF - 1; + } + + /* + Calculate the cost of this group as coded + by each of the coding tables. + */ + for (t = 0; t < nGroups; t++) { + cost[t] = 0; + } + + if (nGroups == 6) { + short cost0, cost1, cost2, cost3, cost4, cost5; + cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0; + for (i = gs; i <= ge; i++) { + short icv = szptr[i]; + cost0 += len[0][icv]; + cost1 += len[1][icv]; + cost2 += len[2][icv]; + cost3 += len[3][icv]; + cost4 += len[4][icv]; + cost5 += len[5][icv]; + } + cost[0] = cost0; + cost[1] = cost1; + cost[2] = cost2; + cost[3] = cost3; + cost[4] = cost4; + cost[5] = cost5; + } else { + for (i = gs; i <= ge; i++) { + short icv = szptr[i]; + for (t = 0; t < nGroups; t++) { + cost[t] += len[t][icv]; + } + } + } + + /* + Find the coding table which is best for this group, + and record its identity in the selector table. + */ + bc = 999999999; + bt = -1; + for (t = 0; t < nGroups; t++) { + if (cost[t] < bc) { + bc = cost[t]; + bt = t; + } + } + totc += bc; + fave[bt]++; + selector[nSelectors] = (char) bt; + nSelectors++; + + /* + Increment the symbol frequencies for the selected table. + */ + for (i = gs; i <= ge; i++) { + rfreq[bt][szptr[i]]++; + } + + gs = ge + 1; + } + + /* + Recompute the tables based on the accumulated frequencies. + */ + for (t = 0; t < nGroups; t++) { + hbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20); + } + } + + rfreq = null; + fave = null; + cost = null; + + if (!(nGroups < 8)) { + panic(); + } + if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / G_SIZE)))) { + panic(); + } + + + /* Compute MTF values for the selectors. */ + { + char[] pos = new char[N_GROUPS]; + char ll_i, tmp2, tmp; + for (i = 0; i < nGroups; i++) { + pos[i] = (char) i; + } + for (i = 0; i < nSelectors; i++) { + ll_i = selector[i]; + j = 0; + tmp = pos[j]; + while (ll_i != tmp) { + j++; + tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + } + pos[0] = tmp; + selectorMtf[i] = (char) j; + } + } + + int[][] code = new int[N_GROUPS][MAX_ALPHA_SIZE]; + + /* Assign actual codes for the tables. */ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (len[t][i] > maxLen) { + maxLen = len[t][i]; + } + if (len[t][i] < minLen) { + minLen = len[t][i]; + } + } + if (maxLen > 20) { + panic(); + } + if (minLen < 1) { + panic(); + } + hbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize); + } + + /* Transmit the mapping table. */ + { + boolean[] inUse16 = new boolean[16]; + for (i = 0; i < 16; i++) { + inUse16[i] = false; + for (j = 0; j < 16; j++) { + if (inUse[i * 16 + j]) { + inUse16[i] = true; + } + } + } + +// nBytes = bytesOut; + for (i = 0; i < 16; i++) { + if (inUse16[i]) { + bsW(1, 1); + } else { + bsW(1, 0); + } + } + + for (i = 0; i < 16; i++) { + if (inUse16[i]) { + for (j = 0; j < 16; j++) { + if (inUse[i * 16 + j]) { + bsW(1, 1); + } else { + bsW(1, 0); + } + } + } + } + + } + + /* Now the selectors. */ +// nBytes = bytesOut; + bsW (3, nGroups); + bsW (15, nSelectors); + for (i = 0; i < nSelectors; i++) { + for (j = 0; j < selectorMtf[i]; j++) { + bsW(1, 1); + } + bsW(1, 0); + } + + /* Now the coding tables. */ +// nBytes = bytesOut; + + for (t = 0; t < nGroups; t++) { + int curr = len[t][0]; + bsW(5, curr); + for (i = 0; i < alphaSize; i++) { + while (curr < len[t][i]) { + bsW(2, 2); + curr++; /* 10 */ + } + while (curr > len[t][i]) { + bsW(2, 3); + curr--; /* 11 */ + } + bsW (1, 0); + } + } + + /* And finally, the block data proper */ +// nBytes = bytesOut; + selCtr = 0; + gs = 0; + while (true) { + if (gs >= nMTF) { + break; + } + ge = gs + G_SIZE - 1; + if (ge >= nMTF) { + ge = nMTF - 1; + } + for (i = gs; i <= ge; i++) { + bsW(len[selector[selCtr]][szptr[i]], + code[selector[selCtr]][szptr[i]]); + } + + gs = ge + 1; + selCtr++; + } + if (!(selCtr == nSelectors)) { + panic(); + } + } + + private void moveToFrontCodeAndSend () throws IOException { + bsPutIntVS(24, origPtr); + generateMTFValues(); + sendMTFValues(); + } + + private OutputStream bsStream; + + private void simpleSort(int lo, int hi, int d) { + int i, j, h, bigN, hp; + int v; + + bigN = hi - lo + 1; + if (bigN < 2) { + return; + } + + hp = 0; + while (incs[hp] < bigN) { + hp++; + } + hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + + i = lo + h; + while (true) { + /* copy 1 */ + if (i > hi) { + break; + } + v = zptr[i]; + j = i; + while (fullGtU(zptr[j - h] + d, v + d)) { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) { + break; + } + } + zptr[j] = v; + i++; + + /* copy 2 */ + if (i > hi) { + break; + } + v = zptr[i]; + j = i; + while (fullGtU(zptr[j - h] + d, v + d)) { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) { + break; + } + } + zptr[j] = v; + i++; + + /* copy 3 */ + if (i > hi) { + break; + } + v = zptr[i]; + j = i; + while (fullGtU(zptr[j - h] + d, v + d)) { + zptr[j] = zptr[j - h]; + j = j - h; + if (j <= (lo + h - 1)) { + break; + } + } + zptr[j] = v; + i++; + + if (workDone > workLimit && firstAttempt) { + return; + } + } + } + } + + private void vswap(int p1, int p2, int n) { + int temp = 0; + while (n > 0) { + temp = zptr[p1]; + zptr[p1] = zptr[p2]; + zptr[p2] = temp; + p1++; + p2++; + n--; + } + } + + private char med3(char a, char b, char c) { + char t; + if (a > b) { + t = a; + a = b; + b = t; + } + if (b > c) { + t = b; + b = c; + c = t; + } + if (a > b) { + b = a; + } + return b; + } + + private static class StackElem { + int ll; + int hh; + int dd; + } + + private void qSort3(int loSt, int hiSt, int dSt) { + int unLo, unHi, ltLo, gtHi, med, n, m; + int sp, lo, hi, d; + StackElem[] stack = new StackElem[QSORT_STACK_SIZE]; + for (int count = 0; count < QSORT_STACK_SIZE; count++) { + stack[count] = new StackElem(); + } + + sp = 0; + + stack[sp].ll = loSt; + stack[sp].hh = hiSt; + stack[sp].dd = dSt; + sp++; + + while (sp > 0) { + if (sp >= QSORT_STACK_SIZE) { + panic(); + } + + sp--; + lo = stack[sp].ll; + hi = stack[sp].hh; + d = stack[sp].dd; + + if (hi - lo < SMALL_THRESH || d > DEPTH_THRESH) { + simpleSort(lo, hi, d); + if (workDone > workLimit && firstAttempt) { + return; + } + continue; + } + + med = med3(block[zptr[lo] + d + 1], + block[zptr[hi ] + d + 1], + block[zptr[(lo + hi) >> 1] + d + 1]); + + unLo = ltLo = lo; + unHi = gtHi = hi; + + while (true) { + while (true) { + if (unLo > unHi) { + break; + } + n = ((int) block[zptr[unLo] + d + 1]) - med; + if (n == 0) { + int temp = 0; + temp = zptr[unLo]; + zptr[unLo] = zptr[ltLo]; + zptr[ltLo] = temp; + ltLo++; + unLo++; + continue; + } + if (n > 0) { + break; + } + unLo++; + } + while (true) { + if (unLo > unHi) { + break; + } + n = ((int) block[zptr[unHi] + d + 1]) - med; + if (n == 0) { + int temp = 0; + temp = zptr[unHi]; + zptr[unHi] = zptr[gtHi]; + zptr[gtHi] = temp; + gtHi--; + unHi--; + continue; + } + if (n < 0) { + break; + } + unHi--; + } + if (unLo > unHi) { + break; + } + int temp = 0; + temp = zptr[unLo]; + zptr[unLo] = zptr[unHi]; + zptr[unHi] = temp; + unLo++; + unHi--; + } + + if (gtHi < ltLo) { + stack[sp].ll = lo; + stack[sp].hh = hi; + stack[sp].dd = d + 1; + sp++; + continue; + } + + n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) : (unLo - ltLo); + vswap(lo, unLo - n, n); + m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) : (gtHi - unHi); + vswap(unLo, hi - m + 1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + stack[sp].ll = lo; + stack[sp].hh = n; + stack[sp].dd = d; + sp++; + + stack[sp].ll = n + 1; + stack[sp].hh = m - 1; + stack[sp].dd = d + 1; + sp++; + + stack[sp].ll = m; + stack[sp].hh = hi; + stack[sp].dd = d; + sp++; + } + } + + private void mainSort() { + int i, j, ss, sb; + int[] runningOrder = new int[256]; + int[] copy = new int[256]; + boolean[] bigDone = new boolean[256]; + int c1, c2; + int numQSorted; + + /* + In the various block-sized structures, live data runs + from 0 to last+NUM_OVERSHOOT_BYTES inclusive. First, + set up the overshoot area for block. + */ + + // if (verbosity >= 4) fprintf ( stderr, " sort initialise ...\n" ); + for (i = 0; i < NUM_OVERSHOOT_BYTES; i++) { + block[last + i + 2] = block[(i % (last + 1)) + 1]; + } + for (i = 0; i <= last + NUM_OVERSHOOT_BYTES; i++) { + quadrant[i] = 0; + } + + block[0] = (char) (block[last + 1]); + + if (last < 4000) { + /* + Use simpleSort(), since the full sorting mechanism + has quite a large constant overhead. + */ + for (i = 0; i <= last; i++) { + zptr[i] = i; + } + firstAttempt = false; + workDone = workLimit = 0; + simpleSort(0, last, 0); + } else { + numQSorted = 0; + for (i = 0; i <= 255; i++) { + bigDone[i] = false; + } + + for (i = 0; i <= 65536; i++) { + ftab[i] = 0; + } + + c1 = block[0]; + for (i = 0; i <= last; i++) { + c2 = block[i + 1]; + ftab[(c1 << 8) + c2]++; + c1 = c2; + } + + for (i = 1; i <= 65536; i++) { + ftab[i] += ftab[i - 1]; + } + + c1 = block[1]; + for (i = 0; i < last; i++) { + c2 = block[i + 2]; + j = (c1 << 8) + c2; + c1 = c2; + ftab[j]--; + zptr[ftab[j]] = i; + } + + j = ((block[last + 1]) << 8) + (block[1]); + ftab[j]--; + zptr[ftab[j]] = last; + + /* + Now ftab contains the first loc of every small bucket. + Calculate the running order, from smallest to largest + big bucket. + */ + + for (i = 0; i <= 255; i++) { + runningOrder[i] = i; + } + + { + int vv; + int h = 1; + do { + h = 3 * h + 1; + } + while (h <= 256); + do { + h = h / 3; + for (i = h; i <= 255; i++) { + vv = runningOrder[i]; + j = i; + while ((ftab[((runningOrder[j - h]) + 1) << 8] + - ftab[(runningOrder[j - h]) << 8]) > + (ftab[((vv) + 1) << 8] - ftab[(vv) << 8])) { + runningOrder[j] = runningOrder[j - h]; + j = j - h; + if (j <= (h - 1)) { + break; + } + } + runningOrder[j] = vv; + } + } while (h != 1); + } + + /* + The main sorting loop. + */ + for (i = 0; i <= 255; i++) { + + /* + Process big buckets, starting with the least full. + */ + ss = runningOrder[i]; + + /* + Complete the big bucket [ss] by quicksorting + any unsorted small buckets [ss, j]. Hopefully + previous pointer-scanning phases have already + completed many of the small buckets [ss, j], so + we don't have to sort them at all. + */ + for (j = 0; j <= 255; j++) { + sb = (ss << 8) + j; + if (!((ftab[sb] & SETMASK) == SETMASK)) { + int lo = ftab[sb] & CLEARMASK; + int hi = (ftab[sb + 1] & CLEARMASK) - 1; + if (hi > lo) { + qSort3(lo, hi, 2); + numQSorted += (hi - lo + 1); + if (workDone > workLimit && firstAttempt) { + return; + } + } + ftab[sb] |= SETMASK; + } + } + + /* + The ss big bucket is now done. Record this fact, + and update the quadrant descriptors. Remember to + update quadrants in the overshoot area too, if + necessary. The "if (i < 255)" test merely skips + this updating for the last bucket processed, since + updating for the last bucket is pointless. + */ + bigDone[ss] = true; + + if (i < 255) { + int bbStart = ftab[ss << 8] & CLEARMASK; + int bbSize = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart; + int shifts = 0; + + while ((bbSize >> shifts) > 65534) { + shifts++; + } + + for (j = 0; j < bbSize; j++) { + int a2update = zptr[bbStart + j]; + int qVal = (j >> shifts); + quadrant[a2update] = qVal; + if (a2update < NUM_OVERSHOOT_BYTES) { + quadrant[a2update + last + 1] = qVal; + } + } + + if (!(((bbSize - 1) >> shifts) <= 65535)) { + panic(); + } + } + + /* + Now scan this big bucket so as to synthesise the + sorted order for small buckets [t, ss] for all t != ss. + */ + for (j = 0; j <= 255; j++) { + copy[j] = ftab[(j << 8) + ss] & CLEARMASK; + } + + for (j = ftab[ss << 8] & CLEARMASK; + j < (ftab[(ss + 1) << 8] & CLEARMASK); j++) { + c1 = block[zptr[j]]; + if (!bigDone[c1]) { + zptr[copy[c1]] = zptr[j] == 0 ? last : zptr[j] - 1; + copy[c1]++; + } + } + + for (j = 0; j <= 255; j++) { + ftab[(j << 8) + ss] |= SETMASK; + } + } + } + } + + private void randomiseBlock() { + int i; + int rNToGo = 0; + int rTPos = 0; + for (i = 0; i < 256; i++) { + inUse[i] = false; + } + + for (i = 0; i <= last; i++) { + if (rNToGo == 0) { + rNToGo = (char) rNums[rTPos]; + rTPos++; + if (rTPos == 512) { + rTPos = 0; + } + } + rNToGo--; + block[i + 1] ^= ((rNToGo == 1) ? 1 : 0); + // handle 16 bit signed numbers + block[i + 1] &= 0xFF; + + inUse[block[i + 1]] = true; + } + } + + private void doReversibleTransformation() { + int i; + + workLimit = workFactor * last; + workDone = 0; + blockRandomised = false; + firstAttempt = true; + + mainSort(); + + if (workDone > workLimit && firstAttempt) { + randomiseBlock(); + workLimit = workDone = 0; + blockRandomised = true; + firstAttempt = false; + mainSort(); + } + + origPtr = -1; + for (i = 0; i <= last; i++) { + if (zptr[i] == 0) { + origPtr = i; + break; + } + } + + if (origPtr == -1) { + panic(); + } + } + + private boolean fullGtU(int i1, int i2) { + int k; + char c1, c2; + int s1, s2; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + i1++; + i2++; + + k = last + 1; + + do { + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + c1 = block[i1 + 1]; + c2 = block[i2 + 1]; + if (c1 != c2) { + return (c1 > c2); + } + s1 = quadrant[i1]; + s2 = quadrant[i2]; + if (s1 != s2) { + return (s1 > s2); + } + i1++; + i2++; + + if (i1 > last) { + i1 -= last; + i1--; + } + if (i2 > last) { + i2 -= last; + i2--; + } + + k -= 4; + workDone++; + } while (k >= 0); + + return false; + } + + /* + Knuth's increments seem to work better + than Incerpi-Sedgewick here. Possibly + because the number of elems to sort is + usually small, typically <= 20. + */ + private int[] incs = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, + 797161, 2391484 }; + + private void allocateCompressStructures () { + int n = baseBlockSize * blockSize100k; + block = new char[(n + 1 + NUM_OVERSHOOT_BYTES)]; + quadrant = new int[(n + NUM_OVERSHOOT_BYTES)]; + zptr = new int[n]; + ftab = new int[65537]; + + if (block == null || quadrant == null || zptr == null + || ftab == null) { + //int totalDraw = (n + 1 + NUM_OVERSHOOT_BYTES) + (n + NUM_OVERSHOOT_BYTES) + n + 65537; + //compressOutOfMemory ( totalDraw, n ); + } + + /* + The back end needs a place to store the MTF values + whilst it calculates the coding tables. We could + put them in the zptr array. However, these values + will fit in a short, so we overlay szptr at the + start of zptr, in the hope of reducing the number + of cache misses induced by the multiple traversals + of the MTF values when calculating coding tables. + Seems to improve compression speed by about 1%. + */ + // szptr = zptr; + + + szptr = new short[2 * n]; + } + + private void generateMTFValues() { + char[] yy = new char[256]; + int i, j; + char tmp; + char tmp2; + int zPend; + int wr; + int EOB; + + makeMaps(); + EOB = nInUse + 1; + + for (i = 0; i <= EOB; i++) { + mtfFreq[i] = 0; + } + + wr = 0; + zPend = 0; + for (i = 0; i < nInUse; i++) { + yy[i] = (char) i; + } + + + for (i = 0; i <= last; i++) { + char ll_i; + + ll_i = unseqToSeq[block[zptr[i]]]; + + j = 0; + tmp = yy[j]; + while (ll_i != tmp) { + j++; + tmp2 = tmp; + tmp = yy[j]; + yy[j] = tmp2; + } + yy[0] = tmp; + + if (j == 0) { + zPend++; + } else { + if (zPend > 0) { + zPend--; + while (true) { + switch (zPend % 2) { + case 0: + szptr[wr] = (short) RUNA; + wr++; + mtfFreq[RUNA]++; + break; + case 1: + szptr[wr] = (short) RUNB; + wr++; + mtfFreq[RUNB]++; + break; + } + if (zPend < 2) { + break; + } + zPend = (zPend - 2) / 2; + } + zPend = 0; + } + szptr[wr] = (short) (j + 1); + wr++; + mtfFreq[j + 1]++; + } + } + + if (zPend > 0) { + zPend--; + while (true) { + switch (zPend % 2) { + case 0: + szptr[wr] = (short) RUNA; + wr++; + mtfFreq[RUNA]++; + break; + case 1: + szptr[wr] = (short) RUNB; + wr++; + mtfFreq[RUNB]++; + break; + } + if (zPend < 2) { + break; + } + zPend = (zPend - 2) / 2; + } + } + + szptr[wr] = (short) EOB; + wr++; + mtfFreq[EOB]++; + + nMTF = wr; + } +} + + diff --git a/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CRC.java b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CRC.java new file mode 100644 index 0000000..ce03d28 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CRC.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * <keiron@aftexsw.com> to whom the Ant project is very grateful for his + * great code. + */ + +package org.bouncycastle.apache.bzip2; + +/** + * A simple class the hold and calculate the CRC for sanity checking + * of the data. + * + * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a> + */ +class CRC { + public static int crc32Table[] = { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, + 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, + 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, + 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, + 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, + 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, + 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, + 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, + 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, + 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, + 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, + 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, + 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, + 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, + 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, + 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, + 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, + 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + + public CRC() { + initialiseCRC(); + } + + void initialiseCRC() { + globalCrc = 0xffffffff; + } + + int getFinalCRC() { + return ~globalCrc; + } + + int getGlobalCRC() { + return globalCrc; + } + + void setGlobalCRC(int newCrc) { + globalCrc = newCrc; + } + + void updateCRC(int inCh) { + int temp = (globalCrc >> 24) ^ inCh; + if (temp < 0) { + temp = 256 + temp; + } + globalCrc = (globalCrc << 8) ^ CRC.crc32Table[temp]; + } + + int globalCrc; +} + diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java index 7281a6a..98ab0d6 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java @@ -11,9 +11,9 @@ public interface BERTags 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 + 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 + 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; diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java index 063e525..8b8d226 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java @@ -160,7 +160,7 @@ public class DERBoolean { if (value.length != 1) { - throw new IllegalArgumentException("byte value should have 1 byte in it"); + throw new IllegalArgumentException("BOOLEAN value should have 1 byte in it"); } if (value[0] == 0) diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java index 2f299ee..9b1ef55 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java @@ -5,6 +5,9 @@ import java.math.BigInteger; import org.bouncycastle.util.Arrays; +/** + * Use ASN1Enumerated instead of this. + */ public class DEREnumerated extends ASN1Primitive { @@ -52,7 +55,7 @@ public class DEREnumerated * @exception IllegalArgumentException if the tagged object cannot * be converted. */ - public static DEREnumerated getInstance( + public static ASN1Enumerated getInstance( ASN1TaggedObject obj, boolean explicit) { @@ -68,18 +71,27 @@ public class DEREnumerated } } + /** + * @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) { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java index 3804450..57cc84a 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java @@ -5,6 +5,9 @@ import java.math.BigInteger; import org.bouncycastle.util.Arrays; +/** + * Use ASN1Integer instead of this, + */ public class DERInteger extends ASN1Primitive { @@ -67,18 +70,27 @@ public class DERInteger } } + /** + * @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) { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java index e1de22a..3d4d04c 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java @@ -6,6 +6,9 @@ import java.math.BigInteger; import org.bouncycastle.util.Arrays; +/** + * Use ASN1ObjectIdentifier instead of this, + */ public class DERObjectIdentifier extends ASN1Primitive { @@ -38,7 +41,22 @@ public class DERObjectIdentifier if (obj instanceof byte[]) { - return ASN1ObjectIdentifier.fromOctetString((byte[])obj); + 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()); @@ -148,6 +166,9 @@ public class DERObjectIdentifier this.body = Arrays.clone(bytes); } + /** + * @deprecated use ASN1ObjectIdentifier constructor. + */ public DERObjectIdentifier( String identifier) { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java index bb8ec4e..b5cc59a 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java @@ -3,20 +3,23 @@ package org.bouncycastle.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 + * Create an empty sequence */ public DLSequence() { } /** - * create a sequence containing one object + * Create a sequence containing one object */ public DLSequence( ASN1Encodable obj) @@ -25,7 +28,7 @@ public class DLSequence } /** - * create a sequence containing a vector of objects. + * Create a sequence containing a vector of objects. */ public DLSequence( ASN1EncodableVector v) @@ -34,7 +37,7 @@ public class DLSequence } /** - * create a sequence containing an array of objects. + * Create a sequence containing an array of objects. */ public DLSequence( ASN1Encodable[] array) @@ -51,7 +54,7 @@ public class DLSequence for (Enumeration e = this.getObjects(); e.hasMoreElements();) { - Object obj = e.nextElement(); + Object obj = e.nextElement(); length += ((ASN1Encodable)obj).toASN1Primitive().toDLObject().encodedLength(); } @@ -65,12 +68,12 @@ public class DLSequence int encodedLength() throws IOException { - int length = getBodyLength(); + int length = getBodyLength(); return 1 + StreamUtil.calculateBodyLength(length) + length; } - /* + /** * A note on the implementation: * <p> * As DL requires the constructed, definite-length model to @@ -82,17 +85,17 @@ public class DLSequence ASN1OutputStream out) throws IOException { - ASN1OutputStream dOut = out.getDLSubStream(); - int length = getBodyLength(); + 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(); + Object obj = e.nextElement(); dOut.writeObject((ASN1Encodable)obj); } } -} +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java index 755754b..91e83fa 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java @@ -4,7 +4,52 @@ import java.io.IOException; import java.util.Enumeration; /** - * A DER encoded set object + * The DLSet encodes ASN.1 SET value without element ordering, + * and always using definite length form. + * <hr> + * <h2>X.690</h2> + * <h3>8: Basic encoding rules</h3> + * <h4>8.11 Encoding of a set value </h4> + * <b>8.11.1</b> The encoding of a set value shall be constructed + * <p/> + * <b>8.11.2</b> The contents octets shall consist of the complete + * encoding of a data value from each of the types listed in the + * ASN.1 definition of the set type, in an order chosen by the sender, + * unless the type was referenced with the keyword + * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>. + * <p/> + * <b>8.11.3</b> The encoding of a data value may, but need not, + * be present for a type which was referenced with the keyword + * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>. + * <blockquote> + * NOTE — The order of data values in a set value is not significant, + * and places no constraints on the order during transfer + * </blockquote> + * <h3>9: Canonical encoding rules</h3> + * <h4>9.3 Set components</h4> + * The encodings of the component values of a set value shall + * appear in an order determined by their tags as specified + * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1. + * Additionally, for the purposes of determining the order in which + * components are encoded when one or more component is an untagged + * choice type, each untagged choice type is ordered as though it + * has a tag equal to that of the smallest tag in that choice type + * or any untagged choice types nested within. + * <h3>10: Distinguished encoding rules</h3> + * <h4>10.3 Set components</h4> + * The encodings of the component values of a set value shall appear + * in an order determined by their tags as specified + * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1. + * <blockquote> + * 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. + * </blockquote> + * <h3>11: Restrictions on BER employed by both CER and DER</h3> + * <h4>11.5 Set and sequence components with default value </h4> + * The encoding of a set value or sequence value shall not include + * an encoding for any component value which is equal to + * its default value. */ public class DLSet extends ASN1Set @@ -54,7 +99,7 @@ public class DLSet for (Enumeration e = this.getObjects(); e.hasMoreElements();) { - Object obj = e.nextElement(); + Object obj = e.nextElement(); length += ((ASN1Encodable)obj).toASN1Primitive().toDLObject().encodedLength(); } @@ -68,12 +113,12 @@ public class DLSet int encodedLength() throws IOException { - int length = getBodyLength(); + int length = getBodyLength(); return 1 + StreamUtil.calculateBodyLength(length) + length; } - /* + /** * A note on the implementation: * <p> * As DL requires the constructed, definite-length model to @@ -85,17 +130,17 @@ public class DLSet ASN1OutputStream out) throws IOException { - ASN1OutputStream dOut = out.getDLSubStream(); - int length = getBodyLength(); + 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(); + Object obj = e.nextElement(); dOut.writeObject((ASN1Encodable)obj); } } -} +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java index 18fc66c..16a6768 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java @@ -2,50 +2,70 @@ package org.bouncycastle.asn1.bc; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle + * <p> + * 1.3.6.1.4.1.22554 + */ public interface BCObjectIdentifiers { /** * iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle - * + *<p> * 1.3.6.1.4.1.22554 */ public static final ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554"); /** * pbe(1) algorithms + * <p> + * 1.3.6.1.4.1.22554.1 */ - public static final ASN1ObjectIdentifier bc_pbe = new ASN1ObjectIdentifier(bc.getId() + ".1"); + public static final ASN1ObjectIdentifier bc_pbe = bc.branch("1"); /** * SHA-1(1) + * <p> + * 1.3.6.1.4.1.22554.1.1 */ - public static final ASN1ObjectIdentifier bc_pbe_sha1 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".1"); + public static final ASN1ObjectIdentifier bc_pbe_sha1 = bc_pbe.branch("1"); - /** - * SHA-2(2) . (SHA-256(1)|SHA-384(2)|SHA-512(3)|SHA-224(4)) - */ - public static final ASN1ObjectIdentifier bc_pbe_sha256 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.1"); - public static final ASN1ObjectIdentifier bc_pbe_sha384 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.2"); - public static final ASN1ObjectIdentifier bc_pbe_sha512 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.3"); - public static final ASN1ObjectIdentifier bc_pbe_sha224 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.4"); + /** 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) */ - public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs5 = new ASN1ObjectIdentifier(bc_pbe_sha1.getId() + ".1"); - public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12 = new ASN1ObjectIdentifier(bc_pbe_sha1.getId() + ".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"); - public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs5 = new ASN1ObjectIdentifier(bc_pbe_sha256.getId() + ".1"); - public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = new ASN1ObjectIdentifier(bc_pbe_sha256.getId() + ".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)) */ - public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc = new ASN1ObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.2"); - public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc = new ASN1ObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.22"); - public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc = new ASN1ObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.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"); - public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = new ASN1ObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.2"); - public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = new ASN1ObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.22"); - public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = new ASN1ObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".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/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java index c43afe6..51aba65 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java @@ -6,10 +6,10 @@ public interface CMPObjectIdentifiers { // RFC 4210 - // id-PasswordBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 13} + /** 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} + /** 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 @@ -52,19 +52,36 @@ public interface CMPObjectIdentifiers // 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 @@ -81,26 +98,44 @@ public interface CMPObjectIdentifiers // 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} + /** 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"); - // id-smime OBJECT IDENTIFIER ::= { iso(1) member-body(2) - // us(840) rsadsi(113549) pkcs(1) pkcs9(9) 16 } - // - // id-ct OBJECT IDENTIFIER ::= { id-smime 1 } -- content types - // - // id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21} + /** + * 1.2.840.113549.1.9.16.1.21 + * <p> + * id-ct OBJECT IDENTIFIER ::= { id-smime 1 } -- content types + * <p> + * 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/bcprov/src/main/java/org/bouncycastle/asn1/cmp/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/package.html deleted file mode 100644 index eb713c9..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and supporting PKIX-CMP as described RFC 2510. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java index b5a2f34..066cf69 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java @@ -10,6 +10,27 @@ import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.DERObjectIdentifier; import org.bouncycastle.asn1.DERSequence; +/** + * <a href="http://tools.ietf.org/html/rfc5652#page-14">RFC 5652</a>: + * Attribute is a pair of OID (as type identifier) + set of values. + * <p> + * <pre> + * Attribute ::= SEQUENCE { + * attrType OBJECT IDENTIFIER, + * attrValues SET OF AttributeValue + * } + * + * AttributeValue ::= ANY + * </pre> + * <p> + * 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. + * <p> + * 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 { @@ -17,7 +38,14 @@ public class Attribute private ASN1Set attrValues; /** - * return an Attribute object from the given object. + * Return an Attribute object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link Attribute} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with Attribute structure inside + * </ul> * * @param o the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -81,12 +109,6 @@ public class Attribute /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * Attribute ::= SEQUENCE { - * attrType OBJECT IDENTIFIER, - * attrValues SET OF AttributeValue - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java index f114623..02b6cc1 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java @@ -11,6 +11,9 @@ import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.DERObjectIdentifier; import org.bouncycastle.asn1.DERSet; +/** + * This is helper tool to construct {@link Attributes} sets. + */ public class AttributeTable { private Hashtable attributes = new Hashtable(); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java index 614e224..e21c8a7 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java @@ -6,6 +6,21 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.DLSet; +/** + * <a href="http://tools.ietf.org/html/rfc5652">RFC 5652</a> defines + * 5 "SET OF Attribute" entities with 5 different names. + * This is common implementation for them all: + * <pre> + * 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 + * </pre> + */ public class Attributes extends ASN1Object { @@ -21,6 +36,19 @@ public class Attributes attributes = new DLSet(v); } + /** + * Return an Attribute set object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link Attributes} object + * <li> {@link org.bouncycastle.asn1.ASN1Set#getInstance(java.lang.Object) ASN1Set} input formats with Attributes structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static Attributes getInstance(Object obj) { if (obj instanceof Attributes) @@ -47,12 +75,8 @@ public class Attributes return rv; } - /** - * <pre> - * Attributes ::= - * SET SIZE(1..MAX) OF Attribute -- according to RFC 5652 - * </pre> - * @return + /** + * Produce an object suitable for an ASN1OutputStream. */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java index 5152dc9..034753f 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java @@ -11,6 +11,27 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.DERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5083">RFC 5083</a>: + * + * CMS AuthEnveloped Data object. + * <p> + * ASN.1: + * <pre> + * 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 } + * </pre> + */ public class AuthEnvelopedData extends ASN1Object { @@ -51,6 +72,12 @@ public class AuthEnvelopedData this.unauthAttrs = unauthAttrs; } + /** + * Constructs AuthEnvelopedData by parsing supplied ASN1Sequence + * <p> + * @param seq An ASN1Sequence with AuthEnvelopedData + * @deprecated use getInstance(). + */ public AuthEnvelopedData( ASN1Sequence seq) { @@ -98,8 +125,14 @@ public class AuthEnvelopedData } /** - * return an AuthEnvelopedData object from a tagged object. + * Return an AuthEnvelopedData object from a tagged object. + * <p> + * Accepted inputs: + * <ul> + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats + * </ul> * + * @param obj the tagged object holding the object we want. * @param explicit true if the object is meant to be explicitly * tagged false otherwise. @@ -114,10 +147,17 @@ public class AuthEnvelopedData } /** - * return an AuthEnvelopedData object from the given object. + * Return an AuthEnvelopedData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link AuthEnvelopedData} object + * <li> {@link ASN1Sequence org.bouncycastle.asn1.ASN1Sequence} input formats with AuthEnvelopedData structure inside + * </ul> * - * @param obj the object we want converted. - * @throws IllegalArgumentException if the object cannot be converted. + * @param obj The object we want converted. + * @throws IllegalArgumentException if the object cannot be converted, or was null. */ public static AuthEnvelopedData getInstance( Object obj) @@ -172,16 +212,6 @@ public class AuthEnvelopedData /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * 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 } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java index 55569a7..8460c33 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java @@ -11,7 +11,7 @@ import org.bouncycastle.asn1.ASN1TaggedObjectParser; import org.bouncycastle.asn1.BERTags; /** - * Produce an object suitable for an ASN1OutputStream. + * Parse {@link AuthEnvelopedData} input stream. * * <pre> * AuthEnvelopedData ::= SEQUENCE { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java index bbf98f1..c0945f3 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java @@ -14,6 +14,30 @@ import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-9.1">RFC 5652</a> section 9.1: + * The AuthenticatedData carries AuthAttributes and other data + * which define what really is being signed. + * <p> + * <pre> + * 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 + * </pre> + */ public class AuthenticatedData extends ASN1Object { @@ -57,6 +81,9 @@ public class AuthenticatedData this.unauthAttrs = unauthAttrs; } + /** + * @deprecated use getInstance() + */ public AuthenticatedData( ASN1Sequence seq) { @@ -102,7 +129,7 @@ public class AuthenticatedData } /** - * return an AuthenticatedData object from a tagged object. + * 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 @@ -118,7 +145,14 @@ public class AuthenticatedData } /** - * return an AuthenticatedData object from the given object. + * Return an AuthenticatedData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link AuthenticatedData} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with AuthenticatedData structure inside + * </ul> * * @param obj the object we want converted. * @throws IllegalArgumentException if the object cannot be converted. @@ -186,24 +220,6 @@ public class AuthenticatedData /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * 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 - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java index fd867e2..ce9aa4f 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java @@ -13,7 +13,7 @@ import org.bouncycastle.asn1.BERTags; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; /** - * Produce an object suitable for an ASN1OutputStream. + * Parse {@link AuthenticatedData} stream. * <pre> * AuthenticatedData ::= SEQUENCE { * version CMSVersion, @@ -127,9 +127,18 @@ public class AuthenticatedDataParser return null; } + /** + * @deprecated use getEncapsulatedContentInfo() + */ public ContentInfoParser getEnapsulatedContentInfo() throws IOException { + return getEncapsulatedContentInfo(); + } + + public ContentInfoParser getEncapsulatedContentInfo() + throws IOException + { if (nextObject == null) { nextObject = seq.readObject(); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CCMParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CCMParameters.java new file mode 100644 index 0000000..3277bb2 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CCMParameters.java @@ -0,0 +1,102 @@ +package org.bouncycastle.asn1.cms; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.util.Arrays; + +/** + * <a href="http://tools.ietf.org/html/rfc5084">RFC 5084</a>: CCMParameters object. + * <p> + * <pre> + CCMParameters ::= SEQUENCE { + aes-nonce OCTET STRING, -- recommended size is 12 octets + aes-ICVlen AES-CCM-ICVlen DEFAULT 12 } + * </pre> + */ +public class CCMParameters + extends ASN1Object +{ + private byte[] nonce; + private int icvLen; + + /** + * Return an CCMParameters object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link org.bouncycastle.asn1.cms.CCMParameters} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with CCMParameters structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static CCMParameters getInstance( + Object obj) + { + if (obj instanceof CCMParameters) + { + return (CCMParameters)obj; + } + else if (obj != null) + { + return new CCMParameters(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private CCMParameters( + ASN1Sequence seq) + { + this.nonce = ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets(); + + if (seq.size() == 2) + { + this.icvLen = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue().intValue(); + } + else + { + this.icvLen = 12; + } + } + + public CCMParameters( + byte[] nonce, + int icvLen) + { + this.nonce = Arrays.clone(nonce); + this.icvLen = icvLen; + } + + public byte[] getNonce() + { + return Arrays.clone(nonce); + } + + public int getIcvLen() + { + return icvLen; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new DEROctetString(nonce)); + + if (icvLen != 12) + { + v.add(new ASN1Integer(icvLen)); + } + + return new DERSequence(v); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java index 5e97324..d2fc7d1 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java @@ -3,11 +3,28 @@ package org.bouncycastle.asn1.cms; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +/** + * <a href="http://tools.ietf.org/html/rfc5652">RFC 5652</a> CMS attribute OID constants. + * <pre> + * 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 + * </pre> + */ + 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 <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */ public static final ASN1ObjectIdentifier contentHint = PKCSObjectIdentifiers.id_aa_contentHint; } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java index 6294d97..b88bf6e 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java @@ -5,24 +5,39 @@ import org.bouncycastle.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 + * <p> + * <pre> * id-ri OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) - * dod(6) internet(1) security(5) mechanisms(5) pkix(7) ri(16) } + * dod(6) internet(1) security(5) mechanisms(5) pkix(7) ri(16) } + * </pre> */ 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/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java index e9d9f67..e546470 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java @@ -10,12 +10,13 @@ import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; /** - * RFC 3274 - CMS Compressed Data. + * <a href="http://tools.ietf.org/html/rfc3274">RFC 3274</a>: CMS Compressed Data. + * * <pre> * CompressedData ::= SEQUENCE { - * version CMSVersion, - * compressionAlgorithm CompressionAlgorithmIdentifier, - * encapContentInfo EncapsulatedContentInfo + * version CMSVersion, + * compressionAlgorithm CompressionAlgorithmIdentifier, + * encapContentInfo EncapsulatedContentInfo * } * </pre> */ @@ -41,27 +42,33 @@ public class CompressedData 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. + * Return a CompressedData object from a tagged object. * - * @param _ato the tagged object holding the object we want. - * @param _explicit true if the object is meant to be explicitly + * @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 _explicit) + ASN1TaggedObject ato, + boolean isExplicit) { - return getInstance(ASN1Sequence.getInstance(_ato, _explicit)); + return getInstance(ASN1Sequence.getInstance(ato, isExplicit)); } /** - * return a CompressedData object from the given object. + * Return a CompressedData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link CompressedData} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with CompressedData structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java index 035e19d..41895ce 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java @@ -7,12 +7,13 @@ import org.bouncycastle.asn1.ASN1SequenceParser; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; /** - * RFC 3274 - CMS Compressed Data. + * Parser of <a href="http://tools.ietf.org/html/rfc3274">RFC 3274</a> {@link CompressedData} object. + * <p> * <pre> * CompressedData ::= SEQUENCE { - * version CMSVersion, - * compressionAlgorithm CompressionAlgorithmIdentifier, - * encapContentInfo EncapsulatedContentInfo + * version CMSVersion, + * compressionAlgorithm CompressionAlgorithmIdentifier, + * encapContentInfo EncapsulatedContentInfo * } * </pre> */ diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java index 345cf2c..2e8e039 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java @@ -10,6 +10,22 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.BERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-3">RFC 5652</a> ContentInfo, and + * <a href="http://tools.ietf.org/html/rfc5652#section-5.2">RFC 5652</a> EncapsulatedContentInfo objects. + * + * <pre> + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL + * } + * + * EncapsulatedContentInfo ::= SEQUENCE { + * eContentType ContentType, + * eContent [0] EXPLICIT OCTET STRING OPTIONAL + * } + * </pre> + */ public class ContentInfo extends ASN1Object implements CMSObjectIdentifiers @@ -17,6 +33,19 @@ public class ContentInfo private ASN1ObjectIdentifier contentType; private ASN1Encodable content; + /** + * Return an ContentInfo object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link ContentInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with ContentInfo structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static ContentInfo getInstance( Object obj) { @@ -84,12 +113,6 @@ public class ContentInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * ContentInfo ::= SEQUENCE { - * contentType ContentType, - * content - * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java index bbc3176..19f0ec8 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java @@ -8,12 +8,12 @@ import org.bouncycastle.asn1.ASN1SequenceParser; import org.bouncycastle.asn1.ASN1TaggedObjectParser; /** - * Produce an object suitable for an ASN1OutputStream. + * <a href="http://tools.ietf.org/html/rfc5652#section-3">RFC 5652</a> {@link ContentInfo} object parser. + * * <pre> * ContentInfo ::= SEQUENCE { - * contentType ContentType, - * content - * [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL } * </pre> */ public class ContentInfoParser diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java index 32b7e40..0f3b906 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java @@ -12,13 +12,13 @@ import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; /** - * RFC 3274 - CMS Digest Data. + * <a href="http://tools.ietf.org/html/rfc5652#section-7">RFC 5652</a> DigestedData object. * <pre> * DigestedData ::= SEQUENCE { - * version CMSVersion, - * digestAlgorithm DigestAlgorithmIdentifier, - * encapContentInfo EncapsulatedContentInfo, - * digest Digest } + * version CMSVersion, + * digestAlgorithm DigestAlgorithmIdentifier, + * encapContentInfo EncapsulatedContentInfo, + * digest Digest } * </pre> */ public class DigestedData @@ -50,23 +50,30 @@ public class DigestedData } /** - * return a CompressedData object from a tagged object. + * Return a DigestedData object from a tagged object. * - * @param _ato the tagged object holding the object we want. - * @param _explicit true if the object is meant to be explicitly + * @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 _explicit) + ASN1TaggedObject ato, + boolean isExplicit) { - return getInstance(ASN1Sequence.getInstance(_ato, _explicit)); + return getInstance(ASN1Sequence.getInstance(ato, isExplicit)); } /** - * return a CompressedData object from the given object. + * Return a DigestedData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link DigestedData} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java index 14265e5..64d887d 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java @@ -11,6 +11,17 @@ import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.BERTaggedObject; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.1">RFC 5652</a> EncryptedContentInfo object. + * + * <pre> + * EncryptedContentInfo ::= SEQUENCE { + * contentType ContentType, + * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, + * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL + * } + * </pre> + */ public class EncryptedContentInfo extends ASN1Object { @@ -47,7 +58,14 @@ public class EncryptedContentInfo } /** - * return an EncryptedContentInfo object from the given object. + * Return an EncryptedContentInfo object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link EncryptedContentInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -84,13 +102,6 @@ public class EncryptedContentInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * EncryptedContentInfo ::= SEQUENCE { - * contentType ContentType, - * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier, - * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java index 1e6f040..77fb0bb 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java @@ -9,6 +9,8 @@ import org.bouncycastle.asn1.ASN1TaggedObjectParser; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; /** + * Parser for <a href="http://tools.ietf.org/html/rfc5652#section-6.1">RFC 5652</a> EncryptedContentInfo object. + * <p> * <pre> * EncryptedContentInfo ::= SEQUENCE { * contentType ContentType, diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java index 9d61b33..2c83958 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java @@ -9,6 +9,16 @@ import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.BERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-8">RFC 5652</a> EncryptedData object. + * <p> + * <pre> + * EncryptedData ::= SEQUENCE { + * version CMSVersion, + * encryptedContentInfo EncryptedContentInfo, + * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } + * </pre> + */ public class EncryptedData extends ASN1Object { @@ -16,6 +26,19 @@ public class EncryptedData private EncryptedContentInfo encryptedContentInfo; private ASN1Set unprotectedAttrs; + /** + * Return an EncryptedData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link EncryptedData} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats + * </ul> + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static EncryptedData getInstance(Object o) { if (o instanceof EncryptedData) @@ -70,12 +93,6 @@ public class EncryptedData } /** - * <pre> - * EncryptedData ::= SEQUENCE { - * version CMSVersion, - * encryptedContentInfo EncryptedContentInfo, - * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL } - * </pre> * @return a basic ASN.1 object representation. */ public ASN1Primitive toASN1Primitive() diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java index 6d8b484..994575a 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java @@ -12,6 +12,18 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.DERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.1">RFC 5652</a> EnvelopedData object. + * <pre> + * EnvelopedData ::= SEQUENCE { + * version CMSVersion, + * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, + * recipientInfos RecipientInfos, + * encryptedContentInfo EncryptedContentInfo, + * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL + * } + * </pre> + */ public class EnvelopedData extends ASN1Object { @@ -78,7 +90,7 @@ public class EnvelopedData } /** - * return an EnvelopedData object from a tagged object. + * 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 @@ -94,7 +106,14 @@ public class EnvelopedData } /** - * return an EnvelopedData object from the given object. + * Return an EnvelopedData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link EnvelopedData} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with EnvelopedData structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -142,15 +161,6 @@ public class EnvelopedData /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * EnvelopedData ::= SEQUENCE { - * version CMSVersion, - * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL, - * recipientInfos RecipientInfos, - * encryptedContentInfo EncryptedContentInfo, - * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java index 73529fd..774813a 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java @@ -10,6 +10,8 @@ import org.bouncycastle.asn1.ASN1TaggedObjectParser; import org.bouncycastle.asn1.BERTags; /** + * Parser of <a href="http://tools.ietf.org/html/rfc5652#section-6.1">RFC 5652</a> {@link EnvelopedData} object. + * <p> * <pre> * EnvelopedData ::= SEQUENCE { * version CMSVersion, diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Evidence.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Evidence.java index c68ec9a..4dcbfde 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Evidence.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Evidence.java @@ -6,6 +6,18 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>: + * Binding Documents with Time-Stamps; Evidence object. + * <p> + * <pre> + * Evidence ::= CHOICE { + * tstEvidence [0] TimeStampTokenEvidence, -- see RFC 3161 + * ersEvidence [1] EvidenceRecord, -- see RFC 4998 + * otherEvidence [2] OtherEvidence + * } + * </pre> + */ public class Evidence extends ASN1Object implements ASN1Choice @@ -25,6 +37,18 @@ public class Evidence } } + /** + * Return an Evidence object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> {@link Evidence} object + * <li> {@link org.bouncycastle.asn1.ASN1TaggedObject#getInstance(java.lang.Object) ASN1TaggedObject} input formats with Evidence data inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static Evidence getInstance(Object obj) { if (obj == null || obj instanceof Evidence) diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/GCMParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/GCMParameters.java new file mode 100644 index 0000000..0f03c87 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/GCMParameters.java @@ -0,0 +1,102 @@ +package org.bouncycastle.asn1.cms; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.util.Arrays; + +/** + * <a href="http://tools.ietf.org/html/rfc5084">RFC 5084</a>: GCMParameters object. + * <p> + * <pre> + GCMParameters ::= SEQUENCE { + aes-nonce OCTET STRING, -- recommended size is 12 octets + aes-ICVlen AES-GCM-ICVlen DEFAULT 12 } + * </pre> + */ +public class GCMParameters + extends ASN1Object +{ + private byte[] nonce; + private int icvLen; + + /** + * Return an GCMParameters object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link org.bouncycastle.asn1.cms.GCMParameters} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with GCMParameters structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static GCMParameters getInstance( + Object obj) + { + if (obj instanceof GCMParameters) + { + return (GCMParameters)obj; + } + else if (obj != null) + { + return new GCMParameters(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private GCMParameters( + ASN1Sequence seq) + { + this.nonce = ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets(); + + if (seq.size() == 2) + { + this.icvLen = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue().intValue(); + } + else + { + this.icvLen = 12; + } + } + + public GCMParameters( + byte[] nonce, + int icvLen) + { + this.nonce = Arrays.clone(nonce); + this.icvLen = icvLen; + } + + public byte[] getNonce() + { + return Arrays.clone(nonce); + } + + public int getIcvLen() + { + return icvLen; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new DEROctetString(nonce)); + + if (icvLen != 12) + { + v.add(new ASN1Integer(icvLen)); + } + + return new DERSequence(v); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java index ad0dbb1..d46cbfb 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java @@ -13,12 +13,37 @@ import org.bouncycastle.asn1.x509.Certificate; import org.bouncycastle.asn1.x509.X509CertificateStructure; import org.bouncycastle.asn1.x509.X509Name; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-10.2.4">RFC 5652</a>: IssuerAndSerialNumber object. + * <p> + * <pre> + * IssuerAndSerialNumber ::= SEQUENCE { + * issuer Name, + * serialNumber CertificateSerialNumber + * } + * + * CertificateSerialNumber ::= INTEGER -- See RFC 5280 + * </pre> + */ public class IssuerAndSerialNumber extends ASN1Object { private X500Name name; private ASN1Integer serialNumber; + /** + * Return an IssuerAndSerialNumber object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link IssuerAndSerialNumber} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with IssuerAndSerialNumber structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static IssuerAndSerialNumber getInstance( Object obj) { @@ -36,7 +61,6 @@ public class IssuerAndSerialNumber /** * @deprecated use getInstance() method. - * @param seq */ public IssuerAndSerialNumber( ASN1Sequence seq) @@ -52,6 +76,9 @@ public class IssuerAndSerialNumber this.serialNumber = certificate.getSerialNumber(); } + /** + * @deprecated use constructor taking Certificate + */ public IssuerAndSerialNumber( X509CertificateStructure certificate) { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java index 67c68ab..0361e9f 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java @@ -10,6 +10,18 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.3">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <p> + * <pre> + * KEKIdentifier ::= SEQUENCE { + * keyIdentifier OCTET STRING, + * date GeneralizedTime OPTIONAL, + * other OtherKeyAttribute OPTIONAL + * } + * </pre> + */ public class KEKIdentifier extends ASN1Object { @@ -56,7 +68,7 @@ public class KEKIdentifier } /** - * return a KEKIdentifier object from a tagged object. + * 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 @@ -72,7 +84,14 @@ public class KEKIdentifier } /** - * return a KEKIdentifier object from the given object. + * Return a KEKIdentifier object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link KEKIdentifier} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with KEKIdentifier structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -110,13 +129,6 @@ public class KEKIdentifier /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * KEKIdentifier ::= SEQUENCE { - * keyIdentifier OCTET STRING, - * date GeneralizedTime OPTIONAL, - * other OtherKeyAttribute OPTIONAL - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java index 6c67772..2d0cfa6 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java @@ -10,6 +10,19 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.3">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <p> + * <pre> + * KEKRecipientInfo ::= SEQUENCE { + * version CMSVersion, -- always set to 4 + * kekid KEKIdentifier, + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + * encryptedKey EncryptedKey + * } + * </pre> + */ public class KEKRecipientInfo extends ASN1Object { @@ -39,7 +52,7 @@ public class KEKRecipientInfo } /** - * return a KEKRecipientInfo object from a tagged object. + * 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 @@ -55,7 +68,14 @@ public class KEKRecipientInfo } /** - * return a KEKRecipientInfo object from the given object. + * Return a KEKRecipientInfo object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link KEKRecipientInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with KEKRecipientInfo structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -63,17 +83,17 @@ public class KEKRecipientInfo public static KEKRecipientInfo getInstance( Object obj) { - if (obj == null || obj instanceof KEKRecipientInfo) + if (obj instanceof KEKRecipientInfo) { return (KEKRecipientInfo)obj; } - if(obj instanceof ASN1Sequence) + if (obj != null) { - return new KEKRecipientInfo((ASN1Sequence)obj); + return new KEKRecipientInfo(ASN1Sequence.getInstance(obj)); } - throw new IllegalArgumentException("Invalid KEKRecipientInfo: " + obj.getClass().getName()); + return null; } public ASN1Integer getVersion() @@ -98,14 +118,6 @@ public class KEKRecipientInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * KEKRecipientInfo ::= SEQUENCE { - * version CMSVersion, -- always set to 4 - * kekid KEKIdentifier, - * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, - * encryptedKey EncryptedKey - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java index 29f455a..6580cd4 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java @@ -7,6 +7,16 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <p> + * <pre> + * KeyAgreeRecipientIdentifier ::= CHOICE { + * issuerAndSerialNumber IssuerAndSerialNumber, + * rKeyId [0] IMPLICIT RecipientKeyIdentifier } + * </pre> + */ public class KeyAgreeRecipientIdentifier extends ASN1Object implements ASN1Choice @@ -15,7 +25,7 @@ public class KeyAgreeRecipientIdentifier private RecipientKeyIdentifier rKeyID; /** - * return an KeyAgreeRecipientIdentifier object from a tagged object. + * 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 @@ -31,7 +41,16 @@ public class KeyAgreeRecipientIdentifier } /** - * return an KeyAgreeRecipientIdentifier object from the given object. + * Return an KeyAgreeRecipientIdentifier object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> {@link KeyAgreeRecipientIdentifier} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with IssuerAndSerialNumber structure inside + * <li> {@link org.bouncycastle.asn1.ASN1TaggedObject#getInstance(java.lang.Object) ASN1TaggedObject} with tag value 0: a KeyAgreeRecipientIdentifier data structure + * </ul> + * <p> + * Note: no byte[] input! * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -84,12 +103,6 @@ public class KeyAgreeRecipientIdentifier /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * KeyAgreeRecipientIdentifier ::= CHOICE { - * issuerAndSerialNumber IssuerAndSerialNumber, - * rKeyId [0] IMPLICIT RecipientKeyIdentifier - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java index c6e5744..224932a 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java @@ -11,6 +11,22 @@ import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <p> + * <pre> + * KeyAgreeRecipientInfo ::= SEQUENCE { + * version CMSVersion, -- always set to 3 + * originator [0] EXPLICIT OriginatorIdentifierOrKey, + * ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL, + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + * recipientEncryptedKeys RecipientEncryptedKeys + * } + * + * UserKeyingMaterial ::= OCTET STRING + * </pre> + */ public class KeyAgreeRecipientInfo extends ASN1Object { @@ -32,7 +48,10 @@ public class KeyAgreeRecipientInfo this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; this.recipientEncryptedKeys = recipientEncryptedKeys; } - + + /** + * @deprecated use getInstance() + */ public KeyAgreeRecipientInfo( ASN1Sequence seq) { @@ -55,7 +74,7 @@ public class KeyAgreeRecipientInfo } /** - * return a KeyAgreeRecipientInfo object from a tagged object. + * 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 @@ -71,7 +90,14 @@ public class KeyAgreeRecipientInfo } /** - * return a KeyAgreeRecipientInfo object from the given object. + * Return a KeyAgreeRecipientInfo object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link KeyAgreeRecipientInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with KeyAgreeRecipientInfo structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -79,19 +105,17 @@ public class KeyAgreeRecipientInfo public static KeyAgreeRecipientInfo getInstance( Object obj) { - if (obj == null || obj instanceof KeyAgreeRecipientInfo) + if (obj instanceof KeyAgreeRecipientInfo) { return (KeyAgreeRecipientInfo)obj; } - if (obj instanceof ASN1Sequence) + if (obj != null) { - return new KeyAgreeRecipientInfo((ASN1Sequence)obj); + return new KeyAgreeRecipientInfo(ASN1Sequence.getInstance(obj)); } - throw new IllegalArgumentException( - "Illegal object in KeyAgreeRecipientInfo: " + obj.getClass().getName()); - + return null; } public ASN1Integer getVersion() @@ -121,17 +145,6 @@ public class KeyAgreeRecipientInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * KeyAgreeRecipientInfo ::= SEQUENCE { - * version CMSVersion, -- always set to 3 - * originator [0] EXPLICIT OriginatorIdentifierOrKey, - * ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL, - * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, - * recipientEncryptedKeys RecipientEncryptedKeys - * } - * - * UserKeyingMaterial ::= OCTET STRING - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java index 8b0a545..7d31111 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java @@ -10,6 +10,18 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.1">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <pre> + * KeyTransRecipientInfo ::= SEQUENCE { + * version CMSVersion, -- always set to 0 or 2 + * rid RecipientIdentifier, + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + * encryptedKey EncryptedKey + * } + * </pre> + */ public class KeyTransRecipientInfo extends ASN1Object { @@ -36,7 +48,10 @@ public class KeyTransRecipientInfo this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; this.encryptedKey = encryptedKey; } - + + /** + * @deprecated use getInstance() + */ public KeyTransRecipientInfo( ASN1Sequence seq) { @@ -47,7 +62,14 @@ public class KeyTransRecipientInfo } /** - * return a KeyTransRecipientInfo object from the given object. + * Return a KeyTransRecipientInfo object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link KeyTransRecipientInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with KeyTransRecipientInfo structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -55,18 +77,17 @@ public class KeyTransRecipientInfo public static KeyTransRecipientInfo getInstance( Object obj) { - if (obj == null || obj instanceof KeyTransRecipientInfo) + if (obj instanceof KeyTransRecipientInfo) { return (KeyTransRecipientInfo)obj; } - if(obj instanceof ASN1Sequence) + if(obj != null) { - return new KeyTransRecipientInfo((ASN1Sequence)obj); + return new KeyTransRecipientInfo(ASN1Sequence.getInstance(obj)); } - throw new IllegalArgumentException( - "Illegal object in KeyTransRecipientInfo: " + obj.getClass().getName()); + return null; } public ASN1Integer getVersion() @@ -91,14 +112,6 @@ public class KeyTransRecipientInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * KeyTransRecipientInfo ::= SEQUENCE { - * version CMSVersion, -- always set to 0 or 2 - * rid RecipientIdentifier, - * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, - * encryptedKey EncryptedKey - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/MetaData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/MetaData.java index 73db22e..667187b 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/MetaData.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/MetaData.java @@ -9,6 +9,19 @@ import org.bouncycastle.asn1.DERIA5String; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERUTF8String; +/** + * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>: + * Binding Documents with Time-Stamps; MetaData object. + * <p> + * <pre> + * MetaData ::= SEQUENCE { + * hashProtected BOOLEAN, + * fileName UTF8String OPTIONAL, + * mediaType IA5String OPTIONAL, + * otherMetaData Attributes OPTIONAL + * } + * </pre> + */ public class MetaData extends ASN1Object { @@ -49,6 +62,19 @@ public class MetaData } } + /** + * Return a MetaData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link MetaData} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with MetaData structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static MetaData getInstance(Object obj) { if (obj instanceof MetaData) @@ -63,17 +89,6 @@ public class MetaData return null; } - /** - * <pre> - * MetaData ::= SEQUENCE { - * hashProtected BOOLEAN, - * fileName UTF8String OPTIONAL, - * mediaType IA5String OPTIONAL, - * otherMetaData Attributes OPTIONAL - * } - * </pre> - * @return - */ public ASN1Primitive toASN1Primitive() { ASN1EncodableVector v = new ASN1EncodableVector(); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java index c7c3ecb..2096be2 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java @@ -9,6 +9,19 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <pre> + * OriginatorIdentifierOrKey ::= CHOICE { + * issuerAndSerialNumber IssuerAndSerialNumber, + * subjectKeyIdentifier [0] SubjectKeyIdentifier, + * originatorKey [1] OriginatorPublicKey + * } + * + * SubjectKeyIdentifier ::= OCTET STRING + * </pre> + */ public class OriginatorIdentifierOrKey extends ASN1Object implements ASN1Choice @@ -52,7 +65,7 @@ public class OriginatorIdentifierOrKey } /** - * return an OriginatorIdentifierOrKey object from a tagged object. + * 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 @@ -74,7 +87,17 @@ public class OriginatorIdentifierOrKey } /** - * return an OriginatorIdentifierOrKey object from the given object. + * Return an OriginatorIdentifierOrKey object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link OriginatorIdentifierOrKey} object + * <li> {@link IssuerAndSerialNumber} object + * <li> {@link SubjectKeyIdentifier} object + * <li> {@link OriginatorPublicKey} object + * <li> {@link org.bouncycastle.asn1.ASN1TaggedObject#getInstance(java.lang.Object) ASN1TaggedObject} input formats with IssuerAndSerialNumber structure inside + * </ul> * * @param o the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -148,15 +171,6 @@ public class OriginatorIdentifierOrKey /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * OriginatorIdentifierOrKey ::= CHOICE { - * issuerAndSerialNumber IssuerAndSerialNumber, - * subjectKeyIdentifier [0] SubjectKeyIdentifier, - * originatorKey [1] OriginatorPublicKey - * } - * - * SubjectKeyIdentifier ::= OCTET STRING - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java index d87054b..96abf7d 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java @@ -9,6 +9,36 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.1">RFC 5652</a>: OriginatorInfo object. + * <pre> + * 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 } + * </pre> + * <p> + * TODO: RevocationInfoChoices / RevocationInfoChoice. + * Constructor using CertificateSet, CertificationInfoChoices + */ public class OriginatorInfo extends ASN1Object { @@ -54,7 +84,7 @@ public class OriginatorInfo } /** - * return an OriginatorInfo object from a tagged object. + * 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 @@ -70,7 +100,14 @@ public class OriginatorInfo } /** - * return an OriginatorInfo object from the given object. + * Return an OriginatorInfo object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link OriginatorInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OriginatorInfo structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -86,7 +123,7 @@ public class OriginatorInfo { return new OriginatorInfo(ASN1Sequence.getInstance(obj)); } - + return null; } @@ -102,12 +139,6 @@ public class OriginatorInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * OriginatorInfo ::= SEQUENCE { - * certs [0] IMPLICIT CertificateSet OPTIONAL, - * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java index 5d95d13..b9bc52f 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java @@ -9,7 +9,17 @@ import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; - +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <p> + * <pre> + * OriginatorPublicKey ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * publicKey BIT STRING + * } + * </pre> + */ public class OriginatorPublicKey extends ASN1Object { @@ -23,7 +33,10 @@ public class OriginatorPublicKey this.algorithm = algorithm; this.publicKey = new DERBitString(publicKey); } - + + /** + * @deprecated use getInstance() + */ public OriginatorPublicKey( ASN1Sequence seq) { @@ -32,7 +45,7 @@ public class OriginatorPublicKey } /** - * return an OriginatorPublicKey object from a tagged object. + * 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 @@ -48,7 +61,14 @@ public class OriginatorPublicKey } /** - * return an OriginatorPublicKey object from the given object. + * Return an OriginatorPublicKey object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link OriginatorPublicKey} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OriginatorPublicKey structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -56,17 +76,17 @@ public class OriginatorPublicKey public static OriginatorPublicKey getInstance( Object obj) { - if (obj == null || obj instanceof OriginatorPublicKey) + if (obj instanceof OriginatorPublicKey) { return (OriginatorPublicKey)obj; } - if (obj instanceof ASN1Sequence) + if (obj != null) { - return new OriginatorPublicKey((ASN1Sequence)obj); + return new OriginatorPublicKey(ASN1Sequence.getInstance(obj)); } - - throw new IllegalArgumentException("Invalid OriginatorPublicKey: " + obj.getClass().getName()); + + return null; } public AlgorithmIdentifier getAlgorithm() @@ -81,12 +101,6 @@ public class OriginatorPublicKey /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * OriginatorPublicKey ::= SEQUENCE { - * algorithm AlgorithmIdentifier, - * publicKey BIT STRING - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java index 1336bb6..7363c81 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java @@ -8,6 +8,16 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERSequence; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-10.2.7">RFC 5652</a>: OtherKeyAttribute object. + * <p> + * <pre> + * OtherKeyAttribute ::= SEQUENCE { + * keyAttrId OBJECT IDENTIFIER, + * keyAttr ANY DEFINED BY keyAttrId OPTIONAL + * } + * </pre> + */ public class OtherKeyAttribute extends ASN1Object { @@ -15,7 +25,14 @@ public class OtherKeyAttribute private ASN1Encodable keyAttr; /** - * return an OtherKeyAttribute object from the given object. + * Return an OtherKeyAttribute object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link OtherKeyAttribute} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OtherKeyAttribute structure inside + * </ul> * * @param o the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -23,19 +40,22 @@ public class OtherKeyAttribute public static OtherKeyAttribute getInstance( Object o) { - if (o == null || o instanceof OtherKeyAttribute) + if (o instanceof OtherKeyAttribute) { return (OtherKeyAttribute)o; } - if (o instanceof ASN1Sequence) + if (o != null) { - return new OtherKeyAttribute((ASN1Sequence)o); + return new OtherKeyAttribute(ASN1Sequence.getInstance(o)); } - throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName()); + return null; } - + + /** + * @deprecated use getInstance() + */ public OtherKeyAttribute( ASN1Sequence seq) { @@ -63,12 +83,6 @@ public class OtherKeyAttribute /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * OtherKeyAttribute ::= SEQUENCE { - * keyAttrId OBJECT IDENTIFIER, - * keyAttr ANY DEFINED BY keyAttrId OPTIONAL - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java index 692c96c..b77b150 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java @@ -9,6 +9,15 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.5">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <pre> + * OtherRecipientInfo ::= SEQUENCE { + * oriType OBJECT IDENTIFIER, + * oriValue ANY DEFINED BY oriType } + * </pre> + */ public class OtherRecipientInfo extends ASN1Object { @@ -25,7 +34,6 @@ public class OtherRecipientInfo /** * @deprecated use getInstance(). - * @param seq */ public OtherRecipientInfo( ASN1Sequence seq) @@ -35,7 +43,7 @@ public class OtherRecipientInfo } /** - * return a OtherRecipientInfo object from a tagged object. + * 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 @@ -51,7 +59,14 @@ public class OtherRecipientInfo } /** - * return a OtherRecipientInfo object from the given object. + * Return a OtherRecipientInfo object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link PasswordRecipientInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OtherRecipientInfo structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -84,11 +99,6 @@ public class OtherRecipientInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * OtherRecipientInfo ::= SEQUENCE { - * oriType OBJECT IDENTIFIER, - * oriValue ANY DEFINED BY oriType } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java index ae6518a..a8348ff 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java @@ -9,6 +9,15 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-10.2.1">RFC 5652</a>: OtherRevocationInfoFormat object. + * <p> + * <pre> + * OtherRevocationInfoFormat ::= SEQUENCE { + * otherRevInfoFormat OBJECT IDENTIFIER, + * otherRevInfo ANY DEFINED BY otherRevInfoFormat } + * </pre> + */ public class OtherRevocationInfoFormat extends ASN1Object { @@ -31,7 +40,7 @@ public class OtherRevocationInfoFormat } /** - * return a OtherRevocationInfoFormat object from a tagged object. + * 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 @@ -47,7 +56,14 @@ public class OtherRevocationInfoFormat } /** - * return a OtherRevocationInfoFormat object from the given object. + * Return a OtherRevocationInfoFormat object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link OtherRevocationInfoFormat} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OtherRevocationInfoFormat structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -80,11 +96,6 @@ public class OtherRevocationInfoFormat /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * OtherRevocationInfoFormat ::= SEQUENCE { - * otherRevInfoFormat OBJECT IDENTIFIER, - * otherRevInfo ANY DEFINED BY otherRevInfoFormat } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java index f325fcd..7ed16cf 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java @@ -11,6 +11,18 @@ import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-10.2.7">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <pre> + * PasswordRecipientInfo ::= SEQUENCE { + * version CMSVersion, -- Always set to 0 + * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier + * OPTIONAL, + * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, + * encryptedKey EncryptedKey } + * </pre> + */ public class PasswordRecipientInfo extends ASN1Object { @@ -38,7 +50,10 @@ public class PasswordRecipientInfo this.keyEncryptionAlgorithm = keyEncryptionAlgorithm; this.encryptedKey = encryptedKey; } - + + /** + * @deprecated use getInstance() method. + */ public PasswordRecipientInfo( ASN1Sequence seq) { @@ -57,7 +72,7 @@ public class PasswordRecipientInfo } /** - * return a PasswordRecipientInfo object from a tagged object. + * 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 @@ -73,7 +88,14 @@ public class PasswordRecipientInfo } /** - * return a PasswordRecipientInfo object from the given object. + * Return a PasswordRecipientInfo object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link PasswordRecipientInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with PasswordRecipientInfo structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -81,17 +103,17 @@ public class PasswordRecipientInfo public static PasswordRecipientInfo getInstance( Object obj) { - if (obj == null || obj instanceof PasswordRecipientInfo) + if (obj instanceof PasswordRecipientInfo) { return (PasswordRecipientInfo)obj; } - if(obj instanceof ASN1Sequence) + if (obj != null) { - return new PasswordRecipientInfo((ASN1Sequence)obj); + return new PasswordRecipientInfo(ASN1Sequence.getInstance(obj)); } - throw new IllegalArgumentException("Invalid PasswordRecipientInfo: " + obj.getClass().getName()); + return null; } public ASN1Integer getVersion() @@ -116,14 +138,6 @@ public class PasswordRecipientInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * PasswordRecipientInfo ::= SEQUENCE { - * version CMSVersion, -- Always set to 0 - * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier - * OPTIONAL, - * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier, - * encryptedKey EncryptedKey } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java index 2f2a173..5062c10 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java @@ -8,7 +8,16 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; - +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <pre> + * RecipientEncryptedKey ::= SEQUENCE { + * rid KeyAgreeRecipientIdentifier, + * encryptedKey EncryptedKey + * } + * </pre> + */ public class RecipientEncryptedKey extends ASN1Object { @@ -23,7 +32,7 @@ public class RecipientEncryptedKey } /** - * return an RecipientEncryptedKey object from a tagged object. + * 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 @@ -39,7 +48,14 @@ public class RecipientEncryptedKey } /** - * return a RecipientEncryptedKey object from the given object. + * Return a RecipientEncryptedKey object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link RecipientEncryptedKey} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with RecipientEncryptedKey structure inside + * </ul> * * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -47,17 +63,17 @@ public class RecipientEncryptedKey public static RecipientEncryptedKey getInstance( Object obj) { - if (obj == null || obj instanceof RecipientEncryptedKey) + if (obj instanceof RecipientEncryptedKey) { return (RecipientEncryptedKey)obj; } - if (obj instanceof ASN1Sequence) + if (obj != null) { - return new RecipientEncryptedKey((ASN1Sequence)obj); + return new RecipientEncryptedKey(ASN1Sequence.getInstance(obj)); } - throw new IllegalArgumentException("Invalid RecipientEncryptedKey: " + obj.getClass().getName()); + return null; } public RecipientEncryptedKey( @@ -80,12 +96,6 @@ public class RecipientEncryptedKey /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * RecipientEncryptedKey ::= SEQUENCE { - * rid KeyAgreeRecipientIdentifier, - * encryptedKey EncryptedKey - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java index 8aa992d..66b154a 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java @@ -8,6 +8,18 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.1">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <pre> + * RecipientIdentifier ::= CHOICE { + * issuerAndSerialNumber IssuerAndSerialNumber, + * subjectKeyIdentifier [0] SubjectKeyIdentifier + * } + * + * SubjectKeyIdentifier ::= OCTET STRING + * </pre> + */ public class RecipientIdentifier extends ASN1Object implements ASN1Choice @@ -33,7 +45,16 @@ public class RecipientIdentifier } /** - * return a RecipientIdentifier object from the given object. + * Return a RecipientIdentifier object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link RecipientIdentifier} object + * <li> {@link IssuerAndSerialNumber} object + * <li> {@link org.bouncycastle.asn1.ASN1OctetString#getInstance(java.lang.Object) ASN1OctetString} input formats (OctetString, byte[]) with value of KeyIdentifier in DER form + * <li> {@link org.bouncycastle.asn1.ASN1Primitive ASN1Primitive} for RecipientIdentifier constructor + * </ul> * * @param o the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -82,14 +103,6 @@ public class RecipientIdentifier /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * RecipientIdentifier ::= CHOICE { - * issuerAndSerialNumber IssuerAndSerialNumber, - * subjectKeyIdentifier [0] SubjectKeyIdentifier - * } - * - * SubjectKeyIdentifier ::= OCTET STRING - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java index 7593a7a..39a7bb2 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java @@ -9,6 +9,19 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <p> + * <pre> + * RecipientInfo ::= CHOICE { + * ktri KeyTransRecipientInfo, + * kari [1] KeyAgreeRecipientInfo, + * kekri [2] KEKRecipientInfo, + * pwri [3] PasswordRecipientInfo, + * ori [4] OtherRecipientInfo } + * </pre> + */ public class RecipientInfo extends ASN1Object implements ASN1Choice @@ -51,6 +64,20 @@ public class RecipientInfo this.info = info; } + /** + * Return a RecipientInfo object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link RecipientInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with RecipientInfo structure inside + * <li> {@link org.bouncycastle.asn1.ASN1TaggedObject#getInstance(java.lang.Object) ASN1TaggedObject} input formats with RecipientInfo structure inside + * </ul> + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static RecipientInfo getInstance( Object o) { @@ -138,14 +165,6 @@ public class RecipientInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * RecipientInfo ::= CHOICE { - * ktri KeyTransRecipientInfo, - * kari [1] KeyAgreeRecipientInfo, - * kekri [2] KEKRecipientInfo, - * pwri [3] PasswordRecipientInfo, - * ori [4] OtherRecipientInfo } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java index 076761b..f0eae59 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java @@ -10,6 +10,20 @@ import org.bouncycastle.asn1.DERGeneralizedTime; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>: + * Content encryption key delivery mechanisms. + * <p> + * <pre> + * RecipientKeyIdentifier ::= SEQUENCE { + * subjectKeyIdentifier SubjectKeyIdentifier, + * date GeneralizedTime OPTIONAL, + * other OtherKeyAttribute OPTIONAL + * } + * + * SubjectKeyIdentifier ::= OCTET STRING + * </pre> + */ public class RecipientKeyIdentifier extends ASN1Object { @@ -43,6 +57,9 @@ public class RecipientKeyIdentifier this(subjectKeyIdentifier, null, null); } + /** + * @deprecated use getInstance() + */ public RecipientKeyIdentifier( ASN1Sequence seq) { @@ -73,38 +90,45 @@ public class RecipientKeyIdentifier } /** - * return a RecipientKeyIdentifier object from a tagged object. + * Return a RecipientKeyIdentifier object from a tagged object. * - * @param _ato the tagged object holding the object we want. - * @param _explicit true if the object is meant to be explicitly + * @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 _explicit) + public static RecipientKeyIdentifier getInstance(ASN1TaggedObject ato, boolean isExplicit) { - return getInstance(ASN1Sequence.getInstance(_ato, _explicit)); + return getInstance(ASN1Sequence.getInstance(ato, isExplicit)); } /** - * return a RecipientKeyIdentifier object from the given object. + * Return a RecipientKeyIdentifier object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link RecipientKeyIdentifier} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with RecipientKeyIdentifier structure inside + * </ul> * - * @param _obj the object we want converted. + * @param obj the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. */ - public static RecipientKeyIdentifier getInstance(Object _obj) + public static RecipientKeyIdentifier getInstance(Object obj) { - if(_obj == null || _obj instanceof RecipientKeyIdentifier) + if (obj instanceof RecipientKeyIdentifier) { - return (RecipientKeyIdentifier)_obj; + return (RecipientKeyIdentifier)obj; } - if(_obj instanceof ASN1Sequence) + if(obj != null) { - return new RecipientKeyIdentifier((ASN1Sequence)_obj); + return new RecipientKeyIdentifier(ASN1Sequence.getInstance(obj)); } - throw new IllegalArgumentException("Invalid RecipientKeyIdentifier: " + _obj.getClass().getName()); + return null; } public ASN1OctetString getSubjectKeyIdentifier() @@ -125,15 +149,6 @@ public class RecipientKeyIdentifier /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * RecipientKeyIdentifier ::= SEQUENCE { - * subjectKeyIdentifier SubjectKeyIdentifier, - * date GeneralizedTime OPTIONAL, - * other OtherKeyAttribute OPTIONAL - * } - * - * SubjectKeyIdentifier ::= OCTET STRING - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java index e9b91eb..52279c3 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java @@ -8,12 +8,35 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5940">RFC 5940</a>: + * Additional Cryptographic Message Syntax (CMS) Revocation Information Choices. + * <p> + * <pre> + * SCVPReqRes ::= SEQUENCE { + * request [0] EXPLICIT ContentInfo OPTIONAL, + * response ContentInfo } + * </pre> + */ public class SCVPReqRes extends ASN1Object { private final ContentInfo request; private final ContentInfo response; + /** + * Return a SCVPReqRes object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link SCVPReqRes} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with SCVPReqRes structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static SCVPReqRes getInstance( Object obj) { @@ -67,11 +90,6 @@ public class SCVPReqRes } /** - * <pre> - * SCVPReqRes ::= SEQUENCE { - * request [0] EXPLICIT ContentInfo OPTIONAL, - * response ContentInfo } - * </pre> * @return the ASN.1 primitive representation. */ public ASN1Primitive toASN1Primitive() diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java index fd2718a..8c7fcc2 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java @@ -16,7 +16,45 @@ import org.bouncycastle.asn1.BERTaggedObject; import org.bouncycastle.asn1.DERTaggedObject; /** - * a signed data object. + * <a href="http://tools.ietf.org/html/rfc5652#section-5.1">RFC 5652</a>: + * <p> + * A signed data object containing multitude of {@link SignerInfo}s. + * <pre> + * 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 + * </pre> + * <p> + * The version calculation uses following ruleset from RFC 3852 section 5.1: + * <pre> + * 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 + * </pre> + * <p> + * @todo Check possible update for this to RFC 5652 level */ public class SignedData extends ASN1Object @@ -35,6 +73,19 @@ public class SignedData private boolean certsBer; private boolean crlsBer; + /** + * Return a SignedData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link SignedData} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with SignedData structure inside + * </ul> + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static SignedData getInstance( Object o) { @@ -68,24 +119,6 @@ public class SignedData } - // RFC3852, 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 - // private ASN1Integer calculateVersion( ASN1ObjectIdentifier contentOid, ASN1Set certs, @@ -257,16 +290,6 @@ public class SignedData /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * SignedData ::= SEQUENCE { - * version CMSVersion, - * digestAlgorithms DigestAlgorithmIdentifiers, - * encapContentInfo EncapsulatedContentInfo, - * certificates [0] IMPLICIT CertificateSet OPTIONAL, - * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, - * signerInfos SignerInfos - * } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java index 6e23b29..df22b8e 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java @@ -11,6 +11,8 @@ import org.bouncycastle.asn1.ASN1TaggedObjectParser; import org.bouncycastle.asn1.BERTags; /** + * Parser for <a href="http://tools.ietf.org/html/rfc5652#section-5.1">RFC 5652</a>: {@link SignedData} object. + * <p> * <pre> * SignedData ::= SEQUENCE { * version CMSVersion, diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java index 37b6b31..2543eb1 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java @@ -8,6 +8,21 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERTaggedObject; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-5.3">RFC 5652</a>: + * Identify who signed the containing {@link SignerInfo} object. + * <p> + * The certificates referred to by this are at containing {@link SignedData} structure. + * <p> + * <pre> + * SignerIdentifier ::= CHOICE { + * issuerAndSerialNumber IssuerAndSerialNumber, + * subjectKeyIdentifier [0] SubjectKeyIdentifier + * } + * + * SubjectKeyIdentifier ::= OCTET STRING + * </pre> + */ public class SignerIdentifier extends ASN1Object implements ASN1Choice @@ -33,7 +48,16 @@ public class SignerIdentifier } /** - * return a SignerIdentifier object from the given object. + * Return a SignerIdentifier object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link SignerIdentifier} object + * <li> {@link IssuerAndSerialNumber} object + * <li> {@link org.bouncycastle.asn1.ASN1OctetString#getInstance(java.lang.Object) ASN1OctetString} input formats with SignerIdentifier structure inside + * <li> {@link org.bouncycastle.asn1.ASN1Primitive ASN1Primitive} for SignerIdentifier constructor. + * </ul> * * @param o the object we want converted. * @exception IllegalArgumentException if the object cannot be converted. @@ -82,14 +106,6 @@ public class SignerIdentifier /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * SignerIdentifier ::= CHOICE { - * issuerAndSerialNumber IssuerAndSerialNumber, - * subjectKeyIdentifier [0] SubjectKeyIdentifier - * } - * - * SubjectKeyIdentifier ::= OCTET STRING - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java index 8aafd67..4209045 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java @@ -15,6 +15,63 @@ import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-5.3">RFC 5652</a>: + * Signature container per Signer, see {@link SignerIdentifier}. + * <pre> + * 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 + * </pre> + */ public class SignerInfo extends ASN1Object { @@ -26,22 +83,44 @@ public class SignerInfo private ASN1OctetString encryptedDigest; private ASN1Set unauthenticatedAttributes; + /** + * Return a SignerInfo object from the given input + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link SignerInfo} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with SignerInfo structure inside + * </ul> + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static SignerInfo getInstance( Object o) throws IllegalArgumentException { - if (o == null || o instanceof SignerInfo) + if (o instanceof SignerInfo) { return (SignerInfo)o; } - else if (o instanceof ASN1Sequence) + else if (o != null) { - return new SignerInfo((ASN1Sequence)o); + return new SignerInfo(ASN1Sequence.getInstance(o)); } - throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName()); + return null; } + /** + * + * @param sid + * @param digAlgorithm CMS knows as 'digestAlgorithm' + * @param authenticatedAttributes CMS knows as 'signedAttrs' + * @param digEncryptionAlgorithm CMS knows as 'signatureAlgorithm' + * @param encryptedDigest CMS knows as 'signature' + * @param unauthenticatedAttributes CMS knows as 'unsignedAttrs' + */ public SignerInfo( SignerIdentifier sid, AlgorithmIdentifier digAlgorithm, @@ -67,6 +146,15 @@ public class SignerInfo this.unauthenticatedAttributes = unauthenticatedAttributes; } + /** + * + * @param sid + * @param digAlgorithm CMS knows as 'digestAlgorithm' + * @param authenticatedAttributes CMS knows as 'signedAttrs' + * @param digEncryptionAlgorithm CMS knows as 'signatureAlgorithm' + * @param encryptedDigest CMS knows as 'signature' + * @param unauthenticatedAttributes CMS knows as 'unsignedAttrs' + */ public SignerInfo( SignerIdentifier sid, AlgorithmIdentifier digAlgorithm, @@ -167,23 +255,6 @@ public class SignerInfo /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * SignerInfo ::= SEQUENCE { - * version Version, - * SignerIdentifier sid, - * digestAlgorithm DigestAlgorithmIdentifier, - * authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL, - * digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier, - * encryptedDigest EncryptedDigest, - * unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL - * } - * - * EncryptedDigest ::= OCTET STRING - * - * DigestAlgorithmIdentifier ::= AlgorithmIdentifier - * - * DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java index 2087248..977fce6 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java @@ -12,6 +12,22 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERGeneralizedTime; import org.bouncycastle.asn1.DERUTCTime; +/** + * <a href="http://tools.ietf.org/html/rfc5652#section-11.3">RFC 5652</a>: + * Dual-mode timestamp format producing either UTCTIme or GeneralizedTime. + * <p> + * <pre> + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + * </pre> + * <p> + * This has a constructor using java.util.Date for input which generates + * a {@link org.bouncycastle.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.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime}. + */ public class Time extends ASN1Object implements ASN1Choice @@ -25,6 +41,9 @@ public class Time return getInstance(obj.getObject()); } + /** + * @deprecated use getInstance() + */ public Time( ASN1Primitive time) { @@ -38,7 +57,7 @@ public class Time } /** - * creates a time object from a given date - if the date is between 1950 + * 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. */ @@ -63,6 +82,20 @@ public class Time } } + /** + * Return a Time object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link Time} object + * <li> {@link org.bouncycastle.asn1.DERUTCTime DERUTCTime} object + * <li> {@link org.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime} object + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static Time getInstance( Object obj) { @@ -82,6 +115,9 @@ public class Time throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); } + /** + * Get the date+tine as a String in full form century format. + */ public String getTime() { if (time instanceof DERUTCTime) @@ -94,6 +130,9 @@ public class Time } } + /** + * Get java.util.Date version of date+time. + */ public Date getDate() { try @@ -115,11 +154,6 @@ public class Time /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * Time ::= CHOICE { - * utcTime UTCTime, - * generalTime GeneralizedTime } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java index ee1044f..f6d8d5a 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java @@ -7,6 +7,16 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x509.CertificateList; +/** + * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a> + * Binding Documents with Time-Stamps; TimeStampAndCRL object. + * <pre> + * TimeStampAndCRL ::= SEQUENCE { + * timeStamp TimeStampToken, -- according to RFC 3161 + * crl CertificateList OPTIONAL -- according to RFC 5280 + * } + * </pre> + */ public class TimeStampAndCRL extends ASN1Object { @@ -27,6 +37,19 @@ public class TimeStampAndCRL } } + /** + * Return a TimeStampAndCRL object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link TimeStampAndCRL} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with TimeStampAndCRL structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static TimeStampAndCRL getInstance(Object obj) { if (obj instanceof TimeStampAndCRL) @@ -57,15 +80,6 @@ public class TimeStampAndCRL return this.crl; } - /** - * <pre> - * TimeStampAndCRL ::= SEQUENCE { - * timeStamp TimeStampToken, -- according to RFC 3161 - * crl CertificateList OPTIONAL -- according to RFC 5280 - * } - * </pre> - * @return - */ public ASN1Primitive toASN1Primitive() { ASN1EncodableVector v = new ASN1EncodableVector(); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java index 6adefbb..5461147 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java @@ -9,6 +9,14 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; +/** + * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a> + * Binding Documents with Time-Stamps; TimeStampTokenEvidence object. + * <pre> + * TimeStampTokenEvidence ::= + * SEQUENCE SIZE(1..MAX) OF TimeStampAndCRL + * </pre> + */ public class TimeStampTokenEvidence extends ASN1Object { @@ -43,6 +51,19 @@ public class TimeStampTokenEvidence return getInstance(ASN1Sequence.getInstance(tagged, explicit)); } + /** + * Return a TimeStampTokenEvidence object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link TimeStampTokenEvidence} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with TimeStampTokenEvidence structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static TimeStampTokenEvidence getInstance(Object obj) { if (obj instanceof TimeStampTokenEvidence) @@ -62,13 +83,6 @@ public class TimeStampTokenEvidence return timeStampAndCRLs; } - /** - * <pre> - * TimeStampTokenEvidence ::= - * SEQUENCE SIZE(1..MAX) OF TimeStampAndCRL - * </pre> - * @return - */ public ASN1Primitive toASN1Primitive() { ASN1EncodableVector v = new ASN1EncodableVector(); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java index ca8b696..f19061e 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java @@ -9,6 +9,20 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.DERIA5String; +/** + * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>: + * Binding Documents with Time-Stamps; TimeStampedData object. + * <p> + * <pre> + * TimeStampedData ::= SEQUENCE { + * version INTEGER { v1(1) }, + * dataUri IA5String OPTIONAL, + * metaData MetaData OPTIONAL, + * content OCTET STRING OPTIONAL, + * temporalEvidence Evidence + * } + * </pre> + */ public class TimeStampedData extends ASN1Object { @@ -47,18 +61,26 @@ public class TimeStampedData this.temporalEvidence = Evidence.getInstance(seq.getObjectAt(index)); } + /** + * Return a TimeStampedData object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link RecipientKeyIdentifier} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with TimeStampedData structure inside + * </ul> + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ public static TimeStampedData getInstance(Object obj) { - if (obj instanceof TimeStampedData) + if (obj == null || obj instanceof TimeStampedData) { return (TimeStampedData)obj; } - else if (obj != null) - { - return new TimeStampedData(ASN1Sequence.getInstance(obj)); - } - - return null; + return new TimeStampedData(ASN1Sequence.getInstance(obj)); } public DERIA5String getDataUri() @@ -81,18 +103,6 @@ public class TimeStampedData return temporalEvidence; } - /** - * <pre> - * TimeStampedData ::= SEQUENCE { - * version INTEGER { v1(1) }, - * dataUri IA5String OPTIONAL, - * metaData MetaData OPTIONAL, - * content OCTET STRING OPTIONAL, - * temporalEvidence Evidence - * } - * </pre> - * @return - */ public ASN1Primitive toASN1Primitive() { ASN1EncodableVector v = new ASN1EncodableVector(); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java index 0d050eb..f53e00f 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java @@ -12,6 +12,20 @@ import org.bouncycastle.asn1.ASN1SequenceParser; import org.bouncycastle.asn1.BERSequence; import org.bouncycastle.asn1.DERIA5String; +/** + * Parser for <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>: + * {@link TimeStampedData} object. + * <p> + * <pre> + * TimeStampedData ::= SEQUENCE { + * version INTEGER { v1(1) }, + * dataUri IA5String OPTIONAL, + * metaData MetaData OPTIONAL, + * content OCTET STRING OPTIONAL, + * temporalEvidence Evidence + * } + * </pre> + */ public class TimeStampedDataParser { private ASN1Integer version; diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java index 7beb6a4..bd7267b 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java @@ -10,6 +10,14 @@ import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.cms.OriginatorPublicKey; +/** + * <a href="http://tools.ietf.org/html/rfc5753">RFC 5753/3278</a>: MQVuserKeyingMaterial object. + * <pre> + * MQVuserKeyingMaterial ::= SEQUENCE { + * ephemeralPublicKey OriginatorPublicKey, + * addedukm [0] EXPLICIT UserKeyingMaterial OPTIONAL } + * </pre> + */ public class MQVuserKeyingMaterial extends ASN1Object { @@ -42,7 +50,7 @@ public class MQVuserKeyingMaterial } /** - * return an MQVuserKeyingMaterial object from a tagged object. + * 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 @@ -58,7 +66,14 @@ public class MQVuserKeyingMaterial } /** - * return an MQVuserKeyingMaterial object from the given object. + * Return an MQVuserKeyingMaterial object from the given object. + * <p> + * Accepted inputs: + * <ul> + * <li> null → null + * <li> {@link MQVuserKeyingMaterial} object + * <li> {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence} with MQVuserKeyingMaterial inside it. + * </ul> * * @param obj the object we want converted. * @throws IllegalArgumentException if the object cannot be converted. @@ -91,11 +106,6 @@ public class MQVuserKeyingMaterial /** * Produce an object suitable for an ASN1OutputStream. - * <pre> - * MQVuserKeyingMaterial ::= SEQUENCE { - * ephemeralPublicKey OriginatorPublicKey, - * addedukm [0] EXPLICIT UserKeyingMaterial OPTIONAL } - * </pre> */ public ASN1Primitive toASN1Primitive() { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/cms/package.html deleted file mode 100644 index c165a7a..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and supporting Cryptographic Message Syntax as described in PKCS#7 and RFC 3369 (formerly RFC 2630). -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java index c36084d..c298a7e 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java @@ -5,17 +5,25 @@ import org.bouncycastle.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 - static final ASN1ObjectIdentifier id_pkip = id_pkix.branch("5"); + /** 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"); - static final ASN1ObjectIdentifier id_regCtrl_regToken = id_regCtrl.branch("1"); - static final ASN1ObjectIdentifier id_regCtrl_authenticator = id_regCtrl.branch("2"); + /** 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"); - static final ASN1ObjectIdentifier id_regCtrl_pkiArchiveOptions = id_regCtrl.branch("4"); + /** 1.3.6.1.5.5.7.1.4 */ + static final ASN1ObjectIdentifier id_regCtrl_pkiArchiveOptions = id_regCtrl.branch("4"); - static final ASN1ObjectIdentifier id_ct_encKeyWithID = new ASN1ObjectIdentifier(PKCSObjectIdentifiers.id_ct + ".21"); + /** 1.2.840.113549.1.9.16.1,21 */ + static final ASN1ObjectIdentifier id_ct_encKeyWithID = PKCSObjectIdentifiers.id_ct.branch("21"); } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java index fb5ae79..c6e8e0d 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java @@ -2,47 +2,100 @@ package org.bouncycastle.asn1.cryptopro; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * <pre> + * GOST Algorithms OBJECT IDENTIFIERS : + * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2)} + * </pre> + */ public interface CryptoProObjectIdentifiers { - // GOST Algorithms OBJECT IDENTIFIERS : - // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2)} - static final ASN1ObjectIdentifier GOST_id = new ASN1ObjectIdentifier("1.2.643.2.2"); + /** 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"); - static final ASN1ObjectIdentifier gostR28147_cbc = new ASN1ObjectIdentifier(GOST_id+".21"); + /** 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"); - static final ASN1ObjectIdentifier gostR3410_94 = new ASN1ObjectIdentifier(GOST_id+".20"); - static final ASN1ObjectIdentifier gostR3410_2001 = new ASN1ObjectIdentifier(GOST_id+".19"); - static final ASN1ObjectIdentifier gostR3411_94_with_gostR3410_94 = new ASN1ObjectIdentifier(GOST_id+".4"); - static final ASN1ObjectIdentifier gostR3411_94_with_gostR3410_2001 = new ASN1ObjectIdentifier(GOST_id+".3"); - - // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) hashes(30) } - static final ASN1ObjectIdentifier gostR3411_94_CryptoProParamSet = new ASN1ObjectIdentifier(GOST_id+".30.1"); - - // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) signs(32) } - static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_A = new ASN1ObjectIdentifier(GOST_id+".32.2"); - static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_B = new ASN1ObjectIdentifier(GOST_id+".32.3"); - static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_C = new ASN1ObjectIdentifier(GOST_id+".32.4"); - static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_D = new ASN1ObjectIdentifier(GOST_id+".32.5"); - - // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) exchanges(33) } - static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_XchA = new ASN1ObjectIdentifier(GOST_id+".33.1"); - static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_XchB = new ASN1ObjectIdentifier(GOST_id+".33.2"); - static final ASN1ObjectIdentifier gostR3410_94_CryptoPro_XchC = new ASN1ObjectIdentifier(GOST_id+".33.3"); - - //{ iso(1) member-body(2)ru(643) rans(2) cryptopro(2) ecc-signs(35) } - static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_A = new ASN1ObjectIdentifier(GOST_id+".35.1"); - static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_B = new ASN1ObjectIdentifier(GOST_id+".35.2"); - static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_C = new ASN1ObjectIdentifier(GOST_id+".35.3"); - - // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) ecc-exchanges(36) } - static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_XchA = new ASN1ObjectIdentifier(GOST_id+".36.0"); - static final ASN1ObjectIdentifier gostR3410_2001_CryptoPro_XchB = new ASN1ObjectIdentifier(GOST_id+".36.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) } + * <p> + * 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) } + * <p> + * 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) } + * <p> + * 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) } + * <p> + * 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) } + * <p> + * 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"); - static final ASN1ObjectIdentifier gost_ElSgDH3410_default = new ASN1ObjectIdentifier(GOST_id+".36.0"); - static final ASN1ObjectIdentifier gost_ElSgDH3410_1 = new ASN1ObjectIdentifier(GOST_id+".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/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java index e203505..fb1d9e9 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java @@ -7,7 +7,6 @@ import java.util.Hashtable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.math.ec.ECCurve; -import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; /** @@ -23,7 +22,7 @@ public class ECGOST3410NamedCurves { 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 @@ -31,33 +30,33 @@ public class ECGOST3410NamedCurves ECDomainParameters ecParams = new ECDomainParameters( curve, - new ECPoint.Fp(curve, - new ECFieldElement.Fp(curve.getQ(),new BigInteger("1")), // x - new ECFieldElement.Fp(curve.getQ(),new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"))), // y + 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")); + mod_p, // p + new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), + new BigInteger("166")); ecParams = new ECDomainParameters( - curve, - new ECPoint.Fp(curve, - new ECFieldElement.Fp(curve.getQ(),new BigInteger("1")), // x - new ECFieldElement.Fp(curve.getQ(),new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"))), // y - mod_q); + 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 @@ -65,30 +64,30 @@ public class ECGOST3410NamedCurves ecParams = new ECDomainParameters( curve, - new ECPoint.Fp(curve, - new ECFieldElement.Fp(mod_p,new BigInteger("1")), // x - new ECFieldElement.Fp(mod_p,new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124"))), // y + 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")); + mod_p, // p + new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"), + new BigInteger("32858")); ecParams = new ECDomainParameters( - curve, - new ECPoint.Fp(curve, - new ECFieldElement.Fp(mod_p,new BigInteger("0")), - new ECFieldElement.Fp(mod_p,new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"))), + 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( @@ -98,19 +97,19 @@ public class ECGOST3410NamedCurves ecParams = new ECDomainParameters( curve, - new ECPoint.Fp(curve, - new ECFieldElement.Fp(mod_p,new BigInteger("0")), // x - new ECFieldElement.Fp(mod_p,new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"))), // y + 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"); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java index a0459c1..124bca6 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java @@ -11,11 +11,14 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERSequence; +/** + * ASN.1 algorithm identifier parameters for GOST-28147 + */ public class GOST28147Parameters extends ASN1Object { - ASN1OctetString iv; - ASN1ObjectIdentifier paramSet; + private ASN1OctetString iv; + private ASN1ObjectIdentifier paramSet; public static GOST28147Parameters getInstance( ASN1TaggedObject obj, @@ -27,19 +30,22 @@ public class GOST28147Parameters public static GOST28147Parameters getInstance( Object obj) { - if(obj == null || obj instanceof GOST28147Parameters) + if (obj instanceof GOST28147Parameters) { return (GOST28147Parameters)obj; } - if(obj instanceof ASN1Sequence) + if (obj != null) { - return new GOST28147Parameters((ASN1Sequence)obj); + return new GOST28147Parameters(ASN1Sequence.getInstance(obj)); } - throw new IllegalArgumentException("Invalid GOST3410Parameter: " + obj.getClass().getName()); + return null; } + /** + * @deprecated use the getInstance() method. This constructor will vanish! + */ public GOST28147Parameters( ASN1Sequence seq) { @@ -69,4 +75,24 @@ public class GOST28147Parameters 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/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java index 0307f50..45d7814 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java @@ -57,6 +57,9 @@ public class GOST3410PublicKeyAlgParameters this.encryptionParamSet = encryptionParamSet; } + /** + * @deprecated use getInstance() + */ public GOST3410PublicKeyAlgParameters( ASN1Sequence seq) { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/package.html deleted file mode 100644 index 2b0af9e..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes for CRYPTO-PRO related objects - such as GOST identifiers. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java index 1a88c34..d5f6ab6 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java @@ -2,35 +2,28 @@ package org.bouncycastle.asn1.dvcs; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * OIDs for <a href="http://tools.ietf.org/html/rfc3029">RFC 3029</a> + * 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"); - // id-pkix OBJECT IDENTIFIER ::= {iso(1) - // identified-organization(3) dod(6) - // internet(1) security(5) mechanisms(5) pkix(7)} - // - // id-smime OBJECT IDENTIFIER ::= { iso(1) member-body(2) - // us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 16 } - public static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7"); - public 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"); - // -- Authority Information Access for DVCS - // - // id-ad-dvcs OBJECT IDENTIFIER ::= {id-pkix id-ad(48) 4} - public 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"); - // -- Key Purpose for DVCS - // - // id-kp-dvcs OBJECT IDENTIFIER ::= {id-pkix id-kp(3) 10} - public 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"); - // id-ct-DVCSRequestData OBJECT IDENTIFIER ::= { id-smime ct(1) 7 } - // id-ct-DVCSResponseData OBJECT IDENTIFIER ::= { id-smime ct(1) 8 } - public static final ASN1ObjectIdentifier id_ct_DVCSRequestData = id_smime.branch("1.7"); - public static final ASN1ObjectIdentifier id_ct_DVCSResponseData = id_smime.branch("1.8"); - - // -- Data validation certificate attribute - // - // id-aa-dvcs-dvc OBJECT IDENTIFIER ::= { id-smime aa(2) 29 } - public static final ASN1ObjectIdentifier id_aa_dvcs_dvc = id_smime.branch("2.29"); + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/package.html deleted file mode 100644 index a941922..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and processing Data Validation and Certification Server (DVCS) protocols as described in RFC 3029. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java index bef8620..77416dc 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java @@ -2,54 +2,109 @@ package org.bouncycastle.asn1.eac; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * German Federal Office for Information Security + * (Bundesamt für Sicherheit in der Informationstechnik) + * <a href="http://www.bsi.bund.de/">http://www.bsi.bund.de/</a> + * <p> + * <a href="https://www.bsi.bund.de/EN/Publications/TechnicalGuidelines/TR03110/BSITR03110.html">BSI TR-03110</a> + * Technical Guideline Advanced Security Mechanisms for Machine Readable Travel Documents + * <p> + * <a href="https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TR03110/TR-03110_v2.1_P3pdf.pdf?__blob=publicationFile">Technical Guideline TR-03110-3</a> + * 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 - // } + /** + * <pre> + * bsi-de OBJECT IDENTIFIER ::= { + * itu-t(0) identified-organization(4) etsi(0) + * reserved(127) etsi-identified-organization(0) 7 + * } + * </pre> + * 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 - // } - static final ASN1ObjectIdentifier id_PK = bsi_de.branch("2.2.1"); + /** + * <pre> + * id-PK OBJECT IDENTIFIER ::= { + * bsi-de protocols(2) smartcard(2) 1 + * } + * </pre> + * OID: 0.4.0.127.0.7.2.2.1 + */ + static final ASN1ObjectIdentifier id_PK = bsi_de.branch("2.2.1"); - static final ASN1ObjectIdentifier id_PK_DH = id_PK.branch("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 - // } - static final ASN1ObjectIdentifier id_CA = bsi_de.branch("2.2.3"); - static final ASN1ObjectIdentifier id_CA_DH = id_CA.branch("1"); - static final ASN1ObjectIdentifier id_CA_DH_3DES_CBC_CBC = id_CA_DH.branch("1"); - static final ASN1ObjectIdentifier id_CA_ECDH = id_CA.branch("2"); + /** + * <pre> + * id-CA OBJECT IDENTIFIER ::= { + * bsi-de protocols(2) smartcard(2) 3 + * } + * </pre> + * 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 - // } + /** + * <pre> + * id-TA OBJECT IDENTIFIER ::= { + * bsi-de protocols(2) smartcard(2) 2 + * } + * </pre> + * OID: 0.4.0.127.0.7.2.2.2 + */ static final ASN1ObjectIdentifier id_TA = bsi_de.branch("2.2.2"); - static final ASN1ObjectIdentifier id_TA_RSA = id_TA.branch("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 */ + 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"); - static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_1 = id_TA_RSA.branch("3"); - 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.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"); - static final ASN1ObjectIdentifier id_TA_RSA_PSS_SHA_512 = id_TA_RSA.branch("6"); - static final ASN1ObjectIdentifier id_TA_ECDSA = id_TA.branch("2"); - static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_1 = id_TA_ECDSA.branch("1"); - static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_224 = id_TA_ECDSA.branch("2"); - static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_256 = id_TA_ECDSA.branch("3"); - static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_384 = id_TA_ECDSA.branch("4"); - static final ASN1ObjectIdentifier id_TA_ECDSA_SHA_512 = id_TA_ECDSA.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"); /** + * <pre> * id-EAC-ePassport OBJECT IDENTIFIER ::= { - * bsi-de applications(3) mrtd(1) roles(2) 1} + * bsi-de applications(3) mrtd(1) roles(2) 1 + * } + * </pre> + * 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/bcprov/src/main/java/org/bouncycastle/asn1/esf/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/esf/package.html deleted file mode 100644 index de27367..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/esf/package.html +++ /dev/null @@ -1,6 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and supporting [ESF] RFC3126 -Electronic Signature Formats for long term electronic signatures. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ess/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/ess/package.html deleted file mode 100644 index 21854b3..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/ess/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and supporting Enhanced Security Services for S/MIME as described RFC 2634 and RFC 5035. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java index 084a020..a329e63 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java @@ -2,29 +2,57 @@ package org.bouncycastle.asn1.gnu; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * GNU project OID collection<p> + * { iso(1) identifier-organization(3) dod(6) internet(1) private(4) } == IETF defined things + */ public interface GNUObjectIdentifiers { - public static final ASN1ObjectIdentifier GNU = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.1"); // GNU Radius - public static final ASN1ObjectIdentifier GnuPG = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2"); // GnuPG (Ägypten) + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java index e9ab8d6..5bfdbab 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java @@ -2,19 +2,59 @@ package org.bouncycastle.asn1.iana; import org.bouncycastle.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)} // - static final ASN1ObjectIdentifier isakmpOakley = new ASN1ObjectIdentifier("1.3.6.1.5.5.8.1"); - static final ASN1ObjectIdentifier hmacMD5 = new ASN1ObjectIdentifier(isakmpOakley + ".1"); - static final ASN1ObjectIdentifier hmacSHA1 = new ASN1ObjectIdentifier(isakmpOakley + ".2"); + /** 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"); - static final ASN1ObjectIdentifier hmacTIGER = new ASN1ObjectIdentifier(isakmpOakley + ".3"); + /** IPSEC ISAKMP-Oakley hmacTIGER OID: 1.3.6.1.5.5.8.1.3 */ + static final ASN1ObjectIdentifier hmacTIGER = isakmpOakley.branch("3"); - static final ASN1ObjectIdentifier hmacRIPEMD160 = new ASN1ObjectIdentifier(isakmpOakley + ".4"); + /** IPSEC ISAKMP-Oakley hmacRIPEMD160 OID: 1.3.6.1.5.5.8.1.4 */ + static final ASN1ObjectIdentifier hmacRIPEMD160 = isakmpOakley.branch("4"); } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java index 0b5da2b..3c271da 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java @@ -2,32 +2,48 @@ package org.bouncycastle.asn1.icao; import org.bouncycastle.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 + /** LDS security object, see ICAO Doc 9303-Volume 2-Section IV-A3.2<p> + * 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 + /** CSCA master list, see TR CSCA Countersigning and Master List issuance<p> + * 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 + /** document type list, see draft TR LDS and PKI Maintenance, par. 3.2.1 <p> + * 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 + /** Active Authentication protocol, see draft TR LDS and PKI Maintenance, par. 5.2.2<p> + * 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 + /** CSCA name change and key reoll-over, see draft TR LDS and PKI Maintenance, par. 3.2.1<p> + * 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/bcprov/src/main/java/org/bouncycastle/asn1/icao/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/icao/package.html deleted file mode 100644 index f2301db..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/icao/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -ICAO ASN.1 classes for electronic passport. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java index bc2ac8d..6b75fde 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java @@ -2,11 +2,16 @@ package org.bouncycastle.asn1.isismtt; import org.bouncycastle.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"); /** @@ -15,29 +20,37 @@ public interface ISISMTTObjectIdentifiers * 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. + * <p> + * 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 - * * <pre> - * DateOfCertGenSyntax ::= GeneralizedTime + * DateOfCertGenSyntax ::= GeneralizedTime * </pre> + * 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. + * <p> + * 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 + * <p> + * OID: 1.3.36.8.3.3 */ static final ASN1ObjectIdentifier id_isismtt_at_admission = id_isismtt_at.branch("3"); @@ -47,33 +60,37 @@ public interface ISISMTTObjectIdentifiers * 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). + * <p> + * 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 + * <p> + * 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 - * * <pre> - * ICCSNSyntax ::= OCTET STRING (SIZE(8..20)) + * ICCSNSyntax ::= OCTET STRING (SIZE(8..20)) * </pre> + * <p> + * 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�. - * + * certificate and that is used as "security anchor". * <pre> - * PKReferenceSyntax ::= OCTET STRING (SIZE(20)) + * PKReferenceSyntax ::= OCTET STRING (SIZE(20)) * </pre> + * <p> + * OID: 1.3.36.8.3.7 */ static final ASN1ObjectIdentifier id_isismtt_at_PKReference = id_isismtt_at.branch("7"); @@ -81,28 +98,28 @@ public interface ISISMTTObjectIdentifiers * Some other restriction regarding the usage of this certificate. May be * used as attribute in attribute certificate or as extension in a * certificate. - * * <pre> - * RestrictionSyntax ::= DirectoryString (SIZE(1..1024)) + * RestrictionSyntax ::= DirectoryString (SIZE(1..1024)) * </pre> + * <p> + * OID: 1.3.36.8.3.8 * * @see org.bouncycastle.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. - * * <pre> - * RetrieveIfAllowed ::= BOOLEAN - * + * RetrieveIfAllowed ::= BOOLEAN * </pre> + * <p> + * OID: 1.3.36.8.3.9 */ static final ASN1ObjectIdentifier id_isismtt_at_retrieveIfAllowed = id_isismtt_at.branch("9"); @@ -110,6 +127,8 @@ public interface ISISMTTObjectIdentifiers * SingleOCSPResponse extension: The certificate requested by the client by * inserting the RetrieveIfAllowed extension in the request, will be * returned in this extension. + * <p> + * OID: 1.3.36.8.3.10 * * @see org.bouncycastle.asn1.isismtt.ocsp.RequestedCertificate */ @@ -117,6 +136,8 @@ public interface ISISMTTObjectIdentifiers /** * Base ObjectIdentifier for naming authorities + * <p> + * OID: 1.3.36.8.3.11 */ static final ASN1ObjectIdentifier id_isismtt_at_namingAuthorities = id_isismtt_at.branch("11"); @@ -127,13 +148,17 @@ public interface ISISMTTObjectIdentifiers * this extension in the responses. * * <pre> - * CertInDirSince ::= GeneralizedTime + * CertInDirSince ::= GeneralizedTime * </pre> + * <p> + * 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. + * <p> + * OID: 1.3.36.8.3.13 * * @see org.bouncycastle.asn1.isismtt.ocsp.CertHash */ @@ -141,11 +166,13 @@ public interface ISISMTTObjectIdentifiers /** * <pre> - * NameAtBirth ::= DirectoryString(SIZE(1..64) + * NameAtBirth ::= DirectoryString(SIZE(1..64) * </pre> * * Used in * {@link org.bouncycastle.asn1.x509.SubjectDirectoryAttributes SubjectDirectoryAttributes} + * <p> + * OID: 1.3.36.8.3.14 */ static final ASN1ObjectIdentifier id_isismtt_at_nameAtBirth = id_isismtt_at.branch("14"); @@ -155,8 +182,10 @@ public interface ISISMTTObjectIdentifiers * extension in a certificate. * * <pre> - * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048)) + * AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048)) * </pre> + * <p> + * OID: 1.3.36.8.3.15 * * @see org.bouncycastle.asn1.isismtt.x509.AdditionalInformationSyntax */ @@ -171,10 +200,11 @@ public interface ISISMTTObjectIdentifiers * 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. - * * <pre> - * LiabilityLimitationFlagSyntax ::= BOOLEAN + * LiabilityLimitationFlagSyntax ::= BOOLEAN * </pre> + * <p> + * 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/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java index 73e0c58..73575f1 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java @@ -2,8 +2,30 @@ package org.bouncycastle.asn1.kisa; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * Korea Information Security Agency (KISA) + * ({iso(1) member-body(2) kr(410) kisa(200004)}) + * <p> + * See <a href="http://tools.ietf.org/html/rfc4010">RFC 4010</a> + * Use of the SEED Encryption Algorithm + * in Cryptographic Message Syntax (CMS), + * and <a href="http://tools.ietf.org/html/rfc4269">RFC 4269</a> + * The SEED Encryption Algorithm + */ public interface KISAObjectIdentifiers { - public static final ASN1ObjectIdentifier id_seedCBC = new ASN1ObjectIdentifier("1.2.410.200004.1.4"); - public static final ASN1ObjectIdentifier id_npki_app_cmsSeed_wrap = new ASN1ObjectIdentifier("1.2.410.200004.7.1.1.1"); + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java index f40a943..8ba2cf5 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java @@ -2,16 +2,27 @@ package org.bouncycastle.asn1.microsoft; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * Microsoft + * <p> + * Object identifier base: + * <pre> + * iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) microsoft(311) + * </pre> + * 1.3.6.1.4.1.311 + */ public interface MicrosoftObjectIdentifiers { - // - // Microsoft - // iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) microsoft(311) - // + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java index debf268..6aff988 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java @@ -8,40 +8,52 @@ 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"); - // - // CZAG - country, zip, age, and gender - // + /** Verisign CZAG (Country,Zip,Age,Gender) Extension OID: 2.16.840.1.113733.1.6.3 */ static final ASN1ObjectIdentifier verisignCzagExtension = verisign.branch("6.3"); - // D&B D-U-N-S number + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/misc/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/misc/package.html deleted file mode 100644 index e3bda64..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Miscellaneous object identifiers and objects. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/package.html deleted file mode 100644 index 40776b0..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding objects used by mozilla. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java index afa93c4..e3613c6 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java @@ -2,59 +2,95 @@ package org.bouncycastle.asn1.nist; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * + * NIST: + * iso/itu(2) joint-assign(16) us(840) organization(1) gov(101) csor(3) + */ public interface NISTObjectIdentifiers { // - // NIST - // iso/itu(2) joint-assign(16) us(840) organization(1) gov(101) csor(3) - - // // 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"); - static final ASN1ObjectIdentifier aes = nistAlgorithm.branch("1"); + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/nist/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/nist/package.html deleted file mode 100644 index 1cdca76..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/nist/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes for NIST related objects. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java index 2e4132a..fa32068 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java @@ -3,15 +3,23 @@ package org.bouncycastle.asn1.ntt; import org.bouncycastle.asn1.ASN1ObjectIdentifier; /** - * From RFC 3657 + * From <a href="http://tools.ietf.org/html/rfc3657">RFC 3657</a> + * Use of the Camellia Encryption Algorithm + * in Cryptographic Message Syntax (CMS) */ public interface NTTObjectIdentifiers { - public static final ASN1ObjectIdentifier id_camellia128_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.2"); - public static final ASN1ObjectIdentifier id_camellia192_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.3"); - public static final ASN1ObjectIdentifier id_camellia256_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.4"); + /** 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"); - public static final ASN1ObjectIdentifier id_camellia128_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.2"); - public static final ASN1ObjectIdentifier id_camellia192_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.3"); - public static final ASN1ObjectIdentifier id_camellia256_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.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/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java index 40b15e9..f8ea8f7 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java @@ -2,21 +2,28 @@ package org.bouncycastle.asn1.ocsp; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * OIDs for <a href="http://tools.ietf.org/html/rfc2560">RFC 2560</a> + * Online Certificate Status Protocol - OCSP. + */ public interface OCSPObjectIdentifiers { - public static final String pkix_ocsp = "1.3.6.1.5.5.7.48.1"; - - public static final ASN1ObjectIdentifier id_pkix_ocsp = new ASN1ObjectIdentifier(pkix_ocsp); - public static final ASN1ObjectIdentifier id_pkix_ocsp_basic = new ASN1ObjectIdentifier(pkix_ocsp + ".1"); + /** 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"); - // - // extensions - // - public static final ASN1ObjectIdentifier id_pkix_ocsp_nonce = new ASN1ObjectIdentifier(pkix_ocsp + ".2"); - public static final ASN1ObjectIdentifier id_pkix_ocsp_crl = new ASN1ObjectIdentifier(pkix_ocsp + ".3"); + /** 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"); - public static final ASN1ObjectIdentifier id_pkix_ocsp_response = new ASN1ObjectIdentifier(pkix_ocsp + ".4"); - public static final ASN1ObjectIdentifier id_pkix_ocsp_nocheck = new ASN1ObjectIdentifier(pkix_ocsp + ".5"); - public static final ASN1ObjectIdentifier id_pkix_ocsp_archive_cutoff = new ASN1ObjectIdentifier(pkix_ocsp + ".6"); - public static final ASN1ObjectIdentifier id_pkix_ocsp_service_locator = new ASN1ObjectIdentifier(pkix_ocsp + ".7"); + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java index 7279ae1..6770050 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java @@ -1,12 +1,12 @@ package org.bouncycastle.asn1.ocsp; import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.ASN1GeneralizedTime; import org.bouncycastle.asn1.ASN1Object; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; -import org.bouncycastle.asn1.DEREnumerated; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.x509.CRLReason; @@ -32,8 +32,8 @@ public class RevokedInfo if (seq.size() > 1) { - this.revocationReason = CRLReason.getInstance(DEREnumerated.getInstance( - (ASN1TaggedObject)seq.getObjectAt(1), true)); + this.revocationReason = CRLReason.getInstance(ASN1Enumerated.getInstance( + (ASN1TaggedObject)seq.getObjectAt(1), true)); } } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/package.html deleted file mode 100644 index 22c560d..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and supporting OCSP objects. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java index c8ce26b..c169c16 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java @@ -2,30 +2,49 @@ package org.bouncycastle.asn1.oiw; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * OIW organization's OIDs: + * <p> + * id-SHA1 OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } + */ public interface OIWObjectIdentifiers { - // id-SHA1 OBJECT IDENTIFIER ::= - // {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } // + /** 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 } - // + /** + * <pre> + * ElGamal Algorithm OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) oiw(14) dirservsig(7) algorithm(2) encryption(1) 1 } + * </pre> + * OID: 1.3.14.7.2.1.1 + */ static final ASN1ObjectIdentifier elGamalAlgorithm = new ASN1ObjectIdentifier("1.3.14.7.2.1.1"); } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/oiw/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/oiw/package.html deleted file mode 100644 index 44eb2fe..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/oiw/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Objects and OID for the support of ISO OIW. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/package.html deleted file mode 100644 index 1ac16a5..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -A library for parsing and writing ASN.1 objects. Support is provided for DER and BER encoding. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java index 65c0fa8..92c4e8f 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java @@ -9,16 +9,39 @@ import org.bouncycastle.asn1.ASN1Object; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; - +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; + +/** + * <pre> + * 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 } + * </pre> + */ 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) { @@ -34,7 +57,13 @@ public class PBKDF2Params 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) @@ -43,6 +72,13 @@ public class PBKDF2Params 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, @@ -53,6 +89,42 @@ public class PBKDF2Params 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) { @@ -63,24 +135,57 @@ public class PBKDF2Params if (e.hasMoreElements()) { - keyLength = (ASN1Integer)e.nextElement(); - } - else - { - keyLength = null; + 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) @@ -91,6 +196,36 @@ public class PBKDF2Params 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(); @@ -103,6 +238,11 @@ public class PBKDF2Params v.add(keyLength); } + if (prf != null && !prf.equals(algid_hmacWithSHA1)) + { + v.add(prf); + } + return new DERSequence(v); } } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java index 405d0b4..05fced0 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java @@ -2,257 +2,389 @@ package org.bouncycastle.asn1.pkcs; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * pkcs-1 OBJECT IDENTIFIER ::=<p> + * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } + * + */ public interface PKCSObjectIdentifiers { - // - // pkcs-1 OBJECT IDENTIFIER ::= { - // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } - // + /** 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} // - static final ASN1ObjectIdentifier md4 = digestAlgorithm.branch("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} // - static final ASN1ObjectIdentifier md5 = digestAlgorithm.branch("5"); + /** 1.2.840.113549.2.5 */ + static final ASN1ObjectIdentifier md5 = digestAlgorithm.branch("5"); - static final ASN1ObjectIdentifier id_hmacWithSHA1 = digestAlgorithm.branch("7"); - static final ASN1ObjectIdentifier id_hmacWithSHA224 = digestAlgorithm.branch("8"); - static final ASN1ObjectIdentifier id_hmacWithSHA256 = digestAlgorithm.branch("9"); - static final ASN1ObjectIdentifier id_hmacWithSHA384 = digestAlgorithm.branch("10"); - static final ASN1ObjectIdentifier id_hmacWithSHA512 = digestAlgorithm.branch("11"); + /** 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 } // - static final String pkcs_7 = "1.2.840.113549.1.7"; - static final ASN1ObjectIdentifier data = new ASN1ObjectIdentifier(pkcs_7 + ".1"); - static final ASN1ObjectIdentifier signedData = new ASN1ObjectIdentifier(pkcs_7 + ".2"); - static final ASN1ObjectIdentifier envelopedData = new ASN1ObjectIdentifier(pkcs_7 + ".3"); - static final ASN1ObjectIdentifier signedAndEnvelopedData = new ASN1ObjectIdentifier(pkcs_7 + ".4"); - static final ASN1ObjectIdentifier digestedData = new ASN1ObjectIdentifier(pkcs_7 + ".5"); - static final ASN1ObjectIdentifier encryptedData = new ASN1ObjectIdentifier(pkcs_7 + ".6"); + /** 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"); - static final ASN1ObjectIdentifier pkcs_9_at_emailAddress = pkcs_9.branch("1"); - static final ASN1ObjectIdentifier pkcs_9_at_unstructuredName = pkcs_9.branch("2"); - static final ASN1ObjectIdentifier pkcs_9_at_contentType = pkcs_9.branch("3"); - static final ASN1ObjectIdentifier pkcs_9_at_messageDigest = pkcs_9.branch("4"); - static final ASN1ObjectIdentifier pkcs_9_at_signingTime = pkcs_9.branch("5"); - static final ASN1ObjectIdentifier pkcs_9_at_counterSignature = pkcs_9.branch("6"); - static final ASN1ObjectIdentifier pkcs_9_at_challengePassword = pkcs_9.branch("7"); + /** 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"); - static final ASN1ObjectIdentifier pkcs_9_at_extensionRequest = pkcs_9.branch("14"); - static final ASN1ObjectIdentifier pkcs_9_at_smimeCapabilities = pkcs_9.branch("15"); - + /** 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"); - /** @deprecated use x509Certificate instead */ + /** 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"); - static final ASN1ObjectIdentifier id_alg_PWRI_KEK = pkcs_9.branch("16.3.9"); - // // 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"); - static final ASN1ObjectIdentifier id_cti_ets_proofOfOrigin = id_cti.branch("1"); - static final ASN1ObjectIdentifier id_cti_ets_proofOfReceipt = id_cti.branch("2"); + /** 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"); - static final ASN1ObjectIdentifier id_cti_ets_proofOfSender = id_cti.branch("4"); + /** 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"); - static final ASN1ObjectIdentifier id_aa_contentHint = id_aa.branch("4"); // See RFC 2634 - static final ASN1ObjectIdentifier id_aa_msgSigDigest = id_aa.branch("5"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.4 - See <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */ + 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} * */ - static final ASN1ObjectIdentifier id_aa_encrypKeyPref = id_aa.branch("11"); - static final ASN1ObjectIdentifier id_aa_signingCertificate = id_aa.branch("12"); + /** 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 <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */ 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 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_signatureTimeStampToken = id_aa.branch("14"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.15 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_sigPolicyId = id_aa.branch("15"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.16 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_commitmentType = id_aa.branch("16"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.17 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_signerLocation = id_aa.branch("17"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.18 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_signerAttr = id_aa.branch("18"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.19 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_otherSigCert = id_aa.branch("19"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.20 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_contentTimestamp = id_aa.branch("20"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.21 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_certificateRefs = id_aa.branch("21"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.22 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_revocationRefs = id_aa.branch("22"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.23 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_certValues = id_aa.branch("23"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.24 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_revocationValues = id_aa.branch("24"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.25 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_escTimeStamp = id_aa.branch("25"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.26 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ static final ASN1ObjectIdentifier id_aa_ets_certCRLTimestamp = id_aa.branch("26"); + /** PKCS#9: 1.2.840.113549.1.9.16.6.2.27 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */ 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; + 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; + 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)} - // + /** + * id-spq OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + * rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-spq(5)}; <p> + * 1.2.840.113549.1.9.16.5 + */ final String id_spq = "1.2.840.113549.1.9.16.5"; - static final ASN1ObjectIdentifier id_spq_ets_uri = new ASN1ObjectIdentifier(id_spq + ".1"); + /** 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"); - static final ASN1ObjectIdentifier pkcs_12PbeIds = pkcs_12.branch("1"); + /** PKCS#12: 1.2.840.113549.1.12.1 */ + static final ASN1ObjectIdentifier pkcs_12PbeIds = pkcs_12.branch("1"); - static final ASN1ObjectIdentifier pbeWithSHAAnd128BitRC4 = pkcs_12PbeIds.branch("1"); - static final ASN1ObjectIdentifier pbeWithSHAAnd40BitRC4 = pkcs_12PbeIds.branch("2"); + /** 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"); - static final ASN1ObjectIdentifier pbeWithSHAAnd128BitRC2_CBC = pkcs_12PbeIds.branch("5"); - static final ASN1ObjectIdentifier pbeWithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6"); + /** 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"); - static final ASN1ObjectIdentifier id_alg_CMSRC2wrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7"); + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java index 515b515..e707fd1 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java @@ -57,7 +57,11 @@ public class RSAESOAEPparams this.maskGenAlgorithm = maskGenAlgorithm; this.pSourceAlgorithm = pSourceAlgorithm; } - + + /** + * @deprecated use getInstance() + * @param seq + */ public RSAESOAEPparams( ASN1Sequence seq) { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/package.html deleted file mode 100644 index ab800f4..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and supporting the various RSA PKCS documents. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java index 4bf6b2b..df2238a 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java @@ -62,7 +62,7 @@ public class ECPrivateKey public ECPrivateKey( BigInteger key, - ASN1Object parameters) + ASN1Encodable parameters) { this(key, null, parameters); } @@ -70,7 +70,7 @@ public class ECPrivateKey public ECPrivateKey( BigInteger key, DERBitString publicKey, - ASN1Object parameters) + ASN1Encodable parameters) { byte[] bytes = BigIntegers.asUnsignedByteArray(key); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java index 44c811b..50a7a63 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java @@ -15,6 +15,21 @@ import org.bouncycastle.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) { @@ -36,7 +51,7 @@ public class SECNamedCurves BigInteger n = fromHex("DB7C2ABF62E35E7628DFAC6561C5"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("02" //+ "09487239995A5EE76B55F9C2F098")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -62,7 +77,7 @@ public class SECNamedCurves BigInteger n = fromHex("36DF0AAFD8B8D7597CA10520D04B"); BigInteger h = BigInteger.valueOf(4); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("03" //+ "4BA30AB5E892B4E1649DD0928643")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -88,7 +103,7 @@ public class SECNamedCurves BigInteger n = fromHex("FFFFFFFE0000000075A30D1B9038A115"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("03" //+ "161FF7528B899B2D0C28607CA52C5B86")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -114,7 +129,7 @@ public class SECNamedCurves BigInteger n = fromHex("3FFFFFFF7FFFFFFFBE0024720613B5A3"); BigInteger h = BigInteger.valueOf(4); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("02" //+ "7B6AA5D85E572983E6FB32A7CDEBC140")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -140,7 +155,7 @@ public class SECNamedCurves BigInteger n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); // ECPoint G = curve.decodePoint(Hex.decode("02" // + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -166,7 +181,7 @@ public class SECNamedCurves BigInteger n = fromHex("0100000000000000000001F4C8F927AED3CA752257"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("02" //+ "4A96B5688EF573284664698968C38BB913CBFC82")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -192,7 +207,7 @@ public class SECNamedCurves BigInteger n = fromHex("0100000000000000000000351EE786A818F3A1A16B"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("02" //+ "52DCB034293A117E1F4FF11B30F7199D3144CE6D")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -218,7 +233,7 @@ public class SECNamedCurves BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("03" //+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -244,7 +259,7 @@ public class SECNamedCurves BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("03" //+ "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -270,7 +285,7 @@ public class SECNamedCurves BigInteger n = fromHex("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("03" //+ "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -296,7 +311,7 @@ public class SECNamedCurves BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("02" //+ "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -322,7 +337,7 @@ public class SECNamedCurves BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("02" //+ "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -348,7 +363,7 @@ public class SECNamedCurves BigInteger n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("03" //+ "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -374,7 +389,7 @@ public class SECNamedCurves BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); //ECPoint G = curve.decodePoint(Hex.decode("03" //+ "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -400,7 +415,8 @@ public class SECNamedCurves BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409"); BigInteger h = BigInteger.valueOf(1); - ECCurve curve = new ECCurve.Fp(p, a, b); + ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b)); + //ECPoint G = curve.decodePoint(Hex.decode("02" //+ "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66")); ECPoint G = curve.decodePoint(Hex.decode("04" @@ -410,7 +426,7 @@ public class SECNamedCurves return new X9ECParameters(curve, G, n, h, S); } }; - + /* * sect113r1 */ @@ -427,7 +443,7 @@ public class SECNamedCurves BigInteger n = fromHex("0100000000000000D9CCEC8A39E56F"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h); + 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" @@ -454,7 +470,7 @@ public class SECNamedCurves BigInteger n = fromHex("010000000000000108789B2496AF93"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h); + 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" @@ -483,7 +499,7 @@ public class SECNamedCurves BigInteger n = fromHex("0400000000000000023123953A9464B54D"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h); + 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" @@ -512,7 +528,7 @@ public class SECNamedCurves BigInteger n = fromHex("0400000000000000016954A233049BA98F"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h); + 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" @@ -541,7 +557,7 @@ public class SECNamedCurves BigInteger n = fromHex("04000000000000000000020108A2E0CC0D99F8A5EF"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h); + 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" @@ -570,7 +586,7 @@ public class SECNamedCurves BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h); + 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" @@ -599,7 +615,7 @@ public class SECNamedCurves BigInteger n = fromHex("040000000000000000000292FE77E70C12A4234C33"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h); + 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" @@ -626,7 +642,7 @@ public class SECNamedCurves BigInteger n = fromHex("01000000000000000000000000C7F34A778F443ACC920EBA49"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h); + 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" @@ -653,7 +669,7 @@ public class SECNamedCurves BigInteger n = fromHex("010000000000000000000000015AAB561B005413CCD4EE99D5"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h); + 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" @@ -680,7 +696,7 @@ public class SECNamedCurves BigInteger n = fromHex("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF"); BigInteger h = BigInteger.valueOf(4); - ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h); + 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" @@ -707,7 +723,7 @@ public class SECNamedCurves BigInteger n = fromHex("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h); + 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" @@ -734,7 +750,7 @@ public class SECNamedCurves BigInteger n = fromHex("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5"); BigInteger h = BigInteger.valueOf(4); - ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h); + 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" @@ -763,7 +779,7 @@ public class SECNamedCurves BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61"); BigInteger h = BigInteger.valueOf(4); - ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h); + 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" @@ -792,7 +808,7 @@ public class SECNamedCurves BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h); + 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" @@ -819,7 +835,7 @@ public class SECNamedCurves BigInteger n = fromHex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF"); BigInteger h = BigInteger.valueOf(4); - ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h); + 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" @@ -846,7 +862,7 @@ public class SECNamedCurves BigInteger n = fromHex("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h); + 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" @@ -875,7 +891,7 @@ public class SECNamedCurves BigInteger n = fromHex("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001"); BigInteger h = BigInteger.valueOf(4); - ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h); + 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" @@ -904,7 +920,7 @@ public class SECNamedCurves BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47"); BigInteger h = BigInteger.valueOf(2); - ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h); + 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" diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java index 8b19cd6..fb60aca 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java @@ -3,48 +3,85 @@ package org.bouncycastle.asn1.sec; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +/** + * Certicom object identifiers + * <pre> + * ellipticCurve OBJECT IDENTIFIER ::= { + * iso(1) identified-organization(3) certicom(132) curve(0) + * } + * </pre> + */ public interface SECObjectIdentifiers { - /** - * ellipticCurve OBJECT IDENTIFIER ::= { - * iso(1) identified-organization(3) certicom(132) curve(0) - * } - */ + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/sec/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/sec/package.html deleted file mode 100644 index 5e34dec..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Classes for support of the SEC standard for Elliptic Curve. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/smime/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/smime/package.html deleted file mode 100644 index d527aba..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/smime/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and supporting S/MIME. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java index 17f0491..ba2f19e 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java @@ -17,14 +17,19 @@ import org.bouncycastle.util.encoders.Hex; */ public class TeleTrusTNamedCurves { + private static ECCurve configureCurve(ECCurve curve) + { + return curve; + } + static X9ECParametersHolder brainpoolP160r1 = new X9ECParametersHolder() { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + ECCurve curve = configureCurve(new ECCurve.Fp( new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620F", 16), // q new BigInteger("340E7BE2A280EB74E2BE61BADA745D97E8F7C300", 16), // a - new BigInteger("1E589A8595423412134FAA2DBDEC95C8D8675E58", 16)); // b + new BigInteger("1E589A8595423412134FAA2DBDEC95C8D8675E58", 16))); // b return new X9ECParameters( curve, @@ -38,11 +43,11 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + 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' + new BigInteger("7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380", 16))); // b' return new X9ECParameters( curve, @@ -56,10 +61,10 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + ECCurve curve = configureCurve(new ECCurve.Fp( new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", 16), // q new BigInteger("6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF", 16), // a - new BigInteger("469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9", 16)); // b + new BigInteger("469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9", 16))); // b return new X9ECParameters( curve, @@ -73,11 +78,11 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + 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' + new BigInteger("13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79", 16))); // b' return new X9ECParameters( curve, @@ -91,10 +96,10 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + ECCurve curve = configureCurve(new ECCurve.Fp( new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", 16), // q new BigInteger("68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43", 16), // a - new BigInteger("2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B", 16)); // b + new BigInteger("2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B", 16))); // b return new X9ECParameters( curve, @@ -107,11 +112,11 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + 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' + new BigInteger("4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D", 16))); // b' return new X9ECParameters( curve, @@ -124,10 +129,10 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + ECCurve curve = configureCurve(new ECCurve.Fp( new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16), // q new BigInteger("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", 16), // a - new BigInteger("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", 16)); // b + new BigInteger("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", 16))); // b return new X9ECParameters( curve, @@ -140,11 +145,11 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + 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' + new BigInteger("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16))); // b' return new X9ECParameters( curve, @@ -157,10 +162,10 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + ECCurve curve = configureCurve(new ECCurve.Fp( new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", 16), // q new BigInteger("3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4", 16), // a - new BigInteger("520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6", 16)); // b + new BigInteger("520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6", 16))); // b return new X9ECParameters( curve, @@ -173,11 +178,11 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + 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' + new BigInteger("A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CEB5B4FEF422340353", 16))); // b' return new X9ECParameters( curve, @@ -190,10 +195,10 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + ECCurve curve = configureCurve(new ECCurve.Fp( new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16), // q new BigInteger("7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826", 16), // a - new BigInteger("4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11", 16)); // b + new BigInteger("4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11", 16))); // b return new X9ECParameters( curve, @@ -206,11 +211,11 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + 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' + new BigInteger("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16))); // b' return new X9ECParameters( curve, @@ -223,10 +228,10 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + ECCurve curve = configureCurve(new ECCurve.Fp( new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16), // q new BigInteger("7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA", 16), // a - new BigInteger("3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723", 16)); // b + new BigInteger("3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723", 16))); // b return new X9ECParameters( curve, @@ -239,11 +244,11 @@ public class TeleTrusTNamedCurves { protected X9ECParameters createParameters() { - ECCurve curve = new ECCurve.Fp( + 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' + new BigInteger("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16))); // b' return new X9ECParameters( curve, diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java index df9a0ff..895f5e8 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java @@ -2,41 +2,74 @@ package org.bouncycastle.asn1.teletrust; import org.bouncycastle.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"); - static final ASN1ObjectIdentifier rsaSignatureWithripemd160 = teleTrusTRSAsignatureAlgorithm.branch("2"); - static final ASN1ObjectIdentifier rsaSignatureWithripemd128 = teleTrusTRSAsignatureAlgorithm.branch("3"); - static final ASN1ObjectIdentifier rsaSignatureWithripemd256 = teleTrusTRSAsignatureAlgorithm.branch("4"); + /** 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"); - static final ASN1ObjectIdentifier ecSign = teleTrusTAlgorithm.branch("3.2"); + /** 1.3.36.3.3.2 */ + static final ASN1ObjectIdentifier ecSign = teleTrusTAlgorithm.branch("3.2"); - static final ASN1ObjectIdentifier ecSignWithSha1 = ecSign.branch("1"); + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/package.html deleted file mode 100644 index 86606c3..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes for TeleTrust related objects. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/package.html deleted file mode 100644 index d6265f0..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and supporting Time Stamp Protocol as described RFC 3161. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java index 353c196..312bacb 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java @@ -34,16 +34,16 @@ public class DSTU4145NamedCurves 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), false); - points[1] = curves[1].createPoint(new BigInteger("7A1F6653786A68192803910A3D30B2A2018B21CD54", 16), new BigInteger("5F49EB26781C0EC6B8909156D98ED435E45FD59918", 16), false); - points[2] = curves[2].createPoint(new BigInteger("4D41A619BCC6EADF0448FA22FAD567A9181D37389CA", 16), new BigInteger("10B51CC12849B234C75E6DD2028BF7FF5C1CE0D991A1", 16), false); - points[3] = curves[3].createPoint(new BigInteger("6BA06FE51464B2BD26DC57F48819BA9954667022C7D03", 16), new BigInteger("25FBC363582DCEC065080CA8287AAFF09788A66DC3A9E", 16), false); - points[4] = curves[4].createPoint(new BigInteger("714114B762F2FF4A7912A6D2AC58B9B5C2FCFE76DAEB7129", 16), new BigInteger("29C41E568B77C617EFE5902F11DB96FA9613CD8D03DB08DA", 16), false); - points[5] = curves[5].createPoint(new BigInteger("3FCDA526B6CDF83BA1118DF35B3C31761D3545F32728D003EEB25EFE96", 16), new BigInteger("9CA8B57A934C54DEEDA9E54A7BBAD95E3B2E91C54D32BE0B9DF96D8D35", 16), false); - points[6] = curves[6].createPoint(new BigInteger("02A29EF207D0E9B6C55CD260B306C7E007AC491CA1B10C62334A9E8DCD8D20FB7", 16), new BigInteger("10686D41FF744D4449FCCF6D8EEA03102E6812C93A9D60B978B702CF156D814EF", 16), false); - points[7] = curves[7].createPoint(new BigInteger("216EE8B189D291A0224984C1E92F1D16BF75CCD825A087A239B276D3167743C52C02D6E7232AA", 16), new BigInteger("5D9306BACD22B7FAEB09D2E049C6E2866C5D1677762A8F2F2DC9A11C7F7BE8340AB2237C7F2A0", 16), false); - points[8] = curves[8].createPoint(new BigInteger("324A6EDDD512F08C49A99AE0D3F961197A76413E7BE81A400CA681E09639B5FE12E59A109F78BF4A373541B3B9A1", 16), new BigInteger("1AB597A5B4477F59E39539007C7F977D1A567B92B043A49C6B61984C3FE3481AAF454CD41BA1F051626442B3C10", 16), false); - points[9] = curves[9].createPoint(new BigInteger("1A62BA79D98133A16BBAE7ED9A8E03C32E0824D57AEF72F88986874E5AAE49C27BED49A2A95058068426C2171E99FD3B43C5947C857D", 16), new BigInteger("70B5E1E14031C1F70BBEFE96BDDE66F451754B4CA5F48DA241F331AA396B8D1839A855C1769B1EA14BA53308B5E2723724E090E02DB9", 16), false); + 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); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java index 0227d2a..8c16620 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java @@ -3,7 +3,6 @@ package org.bouncycastle.asn1.ua; import java.math.BigInteger; import java.util.Random; -import org.bouncycastle.asn1.x9.X9IntegerConverter; import org.bouncycastle.math.ec.ECConstants; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECFieldElement; @@ -14,12 +13,8 @@ import org.bouncycastle.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 X9IntegerConverter converter = new X9IntegerConverter(); - private static BigInteger trace(ECFieldElement fe) { ECFieldElement t = fe; @@ -38,26 +33,23 @@ public abstract class DSTU4145PointEncoder * @return the solution for <code>z<sup>2</sup> + z = beta</code> or * <code>null</code> if no solution exists. */ - private static ECFieldElement solveQuadradicEquation(ECFieldElement beta) + private static ECFieldElement solveQuadraticEquation(ECCurve curve, ECFieldElement beta) { - ECFieldElement.F2m b = (ECFieldElement.F2m)beta; - ECFieldElement zeroElement = new ECFieldElement.F2m( - b.getM(), b.getK1(), b.getK2(), b.getK3(), ECConstants.ZERO); - - if (beta.toBigInteger().equals(ECConstants.ZERO)) + if (beta.isZero()) { - return zeroElement; + return beta; } + ECFieldElement zeroElement = curve.fromBigInteger(ECConstants.ZERO); + ECFieldElement z = null; - ECFieldElement gamma = zeroElement; + ECFieldElement gamma = null; Random rand = new Random(); - int m = b.getM(); + int m = beta.getFieldSize(); do { - ECFieldElement t = new ECFieldElement.F2m(b.getM(), b.getK1(), - b.getK2(), b.getK3(), new BigInteger(m, rand)); + ECFieldElement t = curve.fromBigInteger(new BigInteger(m, rand)); z = zeroElement; ECFieldElement w = beta; for (int i = 1; i <= m - 1; i++) @@ -66,13 +58,13 @@ public abstract class DSTU4145PointEncoder z = z.square().add(w2.multiply(t)); w = w2.add(beta); } - if (!w.toBigInteger().equals(ECConstants.ZERO)) + if (!w.isZero()) { return null; } gamma = z.square().add(z); } - while (gamma.toBigInteger().equals(ECConstants.ZERO)); + while (gamma.isZero()); return z; } @@ -91,12 +83,15 @@ public abstract class DSTU4145PointEncoder return Arrays.copyOfRange(bytes, 1, bytes.length);*/ - int byteCount = converter.getByteLength(Q.getX()); - byte[] bytes = converter.integerToBytes(Q.getX().toBigInteger(), byteCount); + Q = Q.normalize(); + + ECFieldElement x = Q.getAffineXCoord(); + + byte[] bytes = x.getEncoded(); - if (!(Q.getX().toBigInteger().equals(ECConstants.ZERO))) + if (!x.isZero()) { - ECFieldElement y = Q.getY().multiply(Q.getX().invert()); + ECFieldElement y = Q.getAffineYCoord().divide(x); if (trace(y).equals(ECConstants.ONE)) { bytes[bytes.length - 1] |= 0x01; @@ -129,13 +124,12 @@ public abstract class DSTU4145PointEncoder bytes = Arrays.clone(bytes); bytes[bytes.length - 1] ^= 0x01; } - ECCurve.F2m c = (ECCurve.F2m)curve; ECFieldElement xp = curve.fromBigInteger(new BigInteger(1, bytes)); ECFieldElement yp = null; - if (xp.toBigInteger().equals(ECConstants.ZERO)) + if (xp.isZero()) { yp = (ECFieldElement.F2m)curve.getB(); - for (int i = 0; i < c.getM() - 1; i++) + for (int i = 0; i < curve.getFieldSize() - 1; i++) { yp = yp.square(); } @@ -144,14 +138,14 @@ public abstract class DSTU4145PointEncoder { ECFieldElement beta = xp.add(curve.getA()).add( curve.getB().multiply(xp.square().invert())); - ECFieldElement z = solveQuadradicEquation(beta); + ECFieldElement z = solveQuadraticEquation(curve, beta); if (z == null) { throw new RuntimeException("Invalid point compression"); } if (!trace(z).equals(k)) { - z = z.add(curve.fromBigInteger(ECConstants.ONE)); + z = z.addOne(); } yp = xp.multiply(z); } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java index 046bc6f..ccdb34e 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java @@ -2,15 +2,22 @@ package org.bouncycastle.asn1.ua; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * Ukrainian object identifiers + * <p> + * {iso(1) member-body(2) Ukraine(804) root(2) security(1) cryptography(1) pki(1)} + * <p> + * { ... pki-alg(1) pki-alg-sym(3) Dstu4145WithGost34311(1) PB(1)} + * <p> + * DSTU4145 in polynomial basis has 2 oids, one for little-endian representation and one for big-endian + */ public interface UAObjectIdentifiers { - // Ukrainian object identifiers - // {iso(1) member-body(2) Ukraine(804 ) root(2) security(1) cryptography(1) pki(1)} - + /** Base OID: 1.2.804.2.1.1.1 */ static final ASN1ObjectIdentifier UaOid = new ASN1ObjectIdentifier("1.2.804.2.1.1.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 + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/util/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/util/package.html deleted file mode 100644 index 1db893d..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/util/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -An ASN.1 dump utility. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java index 714a32c..6842182 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java @@ -1,6 +1,7 @@ package org.bouncycastle.asn1.x500.style; import java.io.IOException; +import java.util.Enumeration; import java.util.Hashtable; import org.bouncycastle.asn1.ASN1Encodable; @@ -19,8 +20,6 @@ import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; public class BCStyle implements X500NameStyle { - public static final X500NameStyle INSTANCE = new BCStyle(); - /** * country code - StringType(SIZE(2)) */ @@ -273,9 +272,18 @@ public class BCStyle 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) @@ -322,12 +330,12 @@ public class BCStyle public String[] oidToAttrNames(ASN1ObjectIdentifier oid) { - return IETFUtils.findAttrNamesForOID(oid, DefaultLookUp); + return IETFUtils.findAttrNamesForOID(oid, defaultLookUp); } public ASN1ObjectIdentifier attrNameToOID(String attrName) { - return IETFUtils.decodeAttrName(attrName, DefaultLookUp); + return IETFUtils.decodeAttrName(attrName, defaultLookUp); } public boolean areEqual(X500Name name1, X500Name name2) @@ -451,9 +459,23 @@ public class BCStyle buf.append(','); } - IETFUtils.appendRDN(buf, rdns[i], DefaultSymbols); + 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/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java index c73107e..b4f1794 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java @@ -405,7 +405,7 @@ public class IETFUtils int start = 0; if (vBuf.length() > 0) { - while (vBuf.charAt(start) == ' ') + while (vBuf.length() > start && vBuf.charAt(start) == ' ') { vBuf.insert(start, "\\"); start += 2; diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java index 8486989..8c92257 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java @@ -1,6 +1,7 @@ package org.bouncycastle.asn1.x500.style; import java.io.IOException; +import java.util.Enumeration; import java.util.Hashtable; import org.bouncycastle.asn1.ASN1Encodable; @@ -16,8 +17,6 @@ import org.bouncycastle.asn1.x500.X500NameStyle; public class RFC4519Style implements X500NameStyle { - public static final X500NameStyle INSTANCE = new RFC4519Style(); - 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"); @@ -166,9 +165,18 @@ public class RFC4519Style // 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) @@ -211,12 +219,12 @@ public class RFC4519Style public String[] oidToAttrNames(ASN1ObjectIdentifier oid) { - return IETFUtils.findAttrNamesForOID(oid, DefaultLookUp); + return IETFUtils.findAttrNamesForOID(oid, defaultLookUp); } public ASN1ObjectIdentifier attrNameToOID(String attrName) { - return IETFUtils.decodeAttrName(attrName, DefaultLookUp); + return IETFUtils.decodeAttrName(attrName, defaultLookUp); } public boolean areEqual(X500Name name1, X500Name name2) @@ -350,9 +358,23 @@ public class RFC4519Style buf.append(','); } - IETFUtils.appendRDN(buf, rdns[i], DefaultSymbols); + 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/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java index 92aa0f7..73fe7b4 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java @@ -41,7 +41,10 @@ public class AttributeCertificate this.signatureAlgorithm = signatureAlgorithm; this.signatureValue = signatureValue; } - + + /** + * @deprecated use getInstance() method. + */ public AttributeCertificate( ASN1Sequence seq) { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java index 7b9d450..ae539f4 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java @@ -13,7 +13,7 @@ import org.bouncycastle.asn1.DERSequence; public class AttributeCertificateInfo extends ASN1Object { - private ASN1Integer version; + private ASN1Integer version; private Holder holder; private AttCertIssuer issuer; private AlgorithmIdentifier signature; @@ -48,22 +48,33 @@ public class AttributeCertificateInfo private AttributeCertificateInfo( ASN1Sequence seq) { - if (seq.size() < 7 || seq.size() > 9) + if (seq.size() < 6 || seq.size() > 9) { throw new IllegalArgumentException("Bad sequence size: " + seq.size()); } - this.version = ASN1Integer.getInstance(seq.getObjectAt(0)); - this.holder = Holder.getInstance(seq.getObjectAt(1)); - this.issuer = AttCertIssuer.getInstance(seq.getObjectAt(2)); - this.signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(3)); - this.serialNumber = ASN1Integer.getInstance(seq.getObjectAt(4)); - this.attrCertValidityPeriod = AttCertValidityPeriod.getInstance(seq.getObjectAt(5)); - this.attributes = ASN1Sequence.getInstance(seq.getObjectAt(6)); + 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 = 7; i < seq.size(); i++) + for (int i = start + 6; i < seq.size(); i++) { - ASN1Encodable obj = (ASN1Encodable)seq.getObjectAt(i); + ASN1Encodable obj = seq.getObjectAt(i); if (obj instanceof DERBitString) { @@ -143,7 +154,10 @@ public class AttributeCertificateInfo { ASN1EncodableVector v = new ASN1EncodableVector(); - v.add(version); + if (version.getValue().intValue() != 0) + { + v.add(version); + } v.add(holder); v.add(issuer); v.add(signature); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java index 91a37ad..61d7d4a 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java @@ -31,6 +31,8 @@ public class CertificateList TBSCertList tbsCertList; AlgorithmIdentifier sigAlgId; DERBitString sig; + boolean isHashCodeSet = false; + int hashCodeValue; public static CertificateList getInstance( ASN1TaggedObject obj, @@ -54,6 +56,10 @@ public class CertificateList return null; } + /** + * @deprecated use getInstance() method. + * @param seq + */ public CertificateList( ASN1Sequence seq) { @@ -124,4 +130,15 @@ public class CertificateList return new DERSequence(v); } + + public int hashCode() + { + if (!isHashCodeSet) + { + hashCodeValue = super.hashCode(); + isHashCodeSet = true; + } + + return hashCodeValue; + } } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java index e42cefa..4d7fc0b 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java @@ -1,6 +1,7 @@ package org.bouncycastle.asn1.x509; import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; @@ -35,6 +36,17 @@ public class CertificatePolicies } /** + * 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. @@ -71,6 +83,19 @@ public class CertificatePolicies 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. * <pre> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java index dcc1b1f..84d21ca 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java @@ -25,6 +25,13 @@ public class ExtendedKeyUsage 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) @@ -32,6 +39,12 @@ public class ExtendedKeyUsage 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) { @@ -47,11 +60,22 @@ public class ExtendedKeyUsage 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) { @@ -78,6 +102,11 @@ public class ExtendedKeyUsage } } + /** + * Base constructor, from multiple KeyPurposeIds. + * + * @param usages an array of KeyPurposeIds. + */ public ExtendedKeyUsage( KeyPurposeId[] usages) { @@ -103,7 +132,7 @@ public class ExtendedKeyUsage while (e.hasMoreElements()) { - ASN1Primitive o = (ASN1Primitive)e.nextElement(); + KeyPurposeId o = KeyPurposeId.getInstance(e.nextElement()); v.add(o); this.usageTable.put(o, o); @@ -112,6 +141,12 @@ public class ExtendedKeyUsage 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) { @@ -120,7 +155,7 @@ public class ExtendedKeyUsage /** * Returns all extended key usages. - * The returned vector contains DERObjectIdentifiers. + * * @return An array with all key purposes. */ public KeyPurposeId[] getUsages() @@ -135,11 +170,21 @@ public class ExtendedKeyUsage 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/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java index 6ae6e35..e854681 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java @@ -31,9 +31,9 @@ import org.bouncycastle.asn1.DERTaggedObject; * * <pre> * subject CHOICE { - * baseCertificateID [0] IssuerSerial, + * baseCertificateID [0] EXPLICIT IssuerSerial, * -- associated with a Public Key Certificate - * subjectName [1] GeneralNames }, + * subjectName [1] EXPLICIT GeneralNames }, * -- associated with a name * </pre> */ @@ -79,10 +79,10 @@ public class Holder switch (tagObj.getTagNo()) { case 0: - baseCertificateID = IssuerSerial.getInstance(tagObj, false); + baseCertificateID = IssuerSerial.getInstance(tagObj, true); break; case 1: - entityName = GeneralNames.getInstance(tagObj, false); + entityName = GeneralNames.getInstance(tagObj, true); break; default: throw new IllegalArgumentException("unknown tag in Holder"); @@ -234,11 +234,11 @@ public class Holder { if (entityName != null) { - return new DERTaggedObject(false, 1, entityName); + return new DERTaggedObject(true, 1, entityName); } else { - return new DERTaggedObject(false, 0, baseCertificateID); + return new DERTaggedObject(true, 0, baseCertificateID); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java index 8d3036b..fefc939 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java @@ -10,6 +10,7 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.x500.X500Name; public class IssuerSerial extends ASN1Object @@ -59,6 +60,13 @@ public class IssuerSerial } public IssuerSerial( + X500Name issuer, + BigInteger serial) + { + this(new GeneralNames(new GeneralName(issuer)), new ASN1Integer(serial)); + } + + public IssuerSerial( GeneralNames issuer, BigInteger serial) { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java index 2943c0b..d4456b7 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java @@ -74,6 +74,17 @@ public class KeyUsage 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(); diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java new file mode 100644 index 0000000..aeb53f0 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java @@ -0,0 +1,106 @@ +package org.bouncycastle.asn1.x509; + +import java.math.BigInteger; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; + +/** + * PKIX RFC 5280 + * <pre> + * id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 } + * + * PolicyConstraints ::= SEQUENCE { + * requireExplicitPolicy [0] SkipCerts OPTIONAL, + * inhibitPolicyMapping [1] SkipCerts OPTIONAL } + * + * SkipCerts ::= INTEGER (0..MAX) + * </pre> + */ +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/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java index 295accf..fe09169 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java @@ -57,7 +57,8 @@ public class PolicyQualifierInfo * Creates a new <code>PolicyQualifierInfo</code> instance. * * @param as <code>PolicyQualifierInfo</code> X509 structure - * encoded as an ASN1Sequence. + * encoded as an ASN1Sequence. + * @deprecated use PolicyQualifierInfo.getInstance() */ public PolicyQualifierInfo( ASN1Sequence as) diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java index f020bcb..f29284d 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java @@ -10,6 +10,7 @@ import org.bouncycastle.asn1.DERBoolean; /** * an object for the elements in the X.509 V3 extension block. + * @deprecated use Extension */ public class X509Extension { diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java index af2c9a9..ff7af8c 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java @@ -1238,49 +1238,47 @@ public class X509Name buf.append('='); - int index = buf.length(); - int start = index; - + int start = buf.length(); buf.append(value); - - int end = buf.length(); + int end = buf.length(); if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#') { - index += 2; - } - - while (index != end) - { - if ((buf.charAt(index) == ',') - || (buf.charAt(index) == '"') - || (buf.charAt(index) == '\\') - || (buf.charAt(index) == '+') - || (buf.charAt(index) == '=') - || (buf.charAt(index) == '<') - || (buf.charAt(index) == '>') - || (buf.charAt(index) == ';')) - { - buf.insert(index, "\\"); - index++; - end++; - } - - index++; + start += 2; } - while (buf.charAt(start) == ' ') + while (start < end && buf.charAt(start) == ' ') { buf.insert(start, "\\"); start += 2; + ++end; } - int endBuf = buf.length() - 1; + while (--end > start && buf.charAt(end) == ' ') + { + buf.insert(end, '\\'); + } - while (endBuf >= 0 && buf.charAt(endBuf) == ' ') + while (start <= end) { - buf.insert(endBuf, '\\'); - endBuf--; + switch (buf.charAt(start)) + { + case ',': + case '"': + case '\\': + case '+': + case '=': + case '<': + case '>': + case ';': + buf.insert(start, "\\"); + start += 2; + ++end; + break; + default: + ++start; + break; + } } } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java index ed4dd32..e1c7a54 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java @@ -4,64 +4,78 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier; public interface X509ObjectIdentifiers { - // - // base id - // - static final String id = "2.5.4"; - - static final ASN1ObjectIdentifier commonName = new ASN1ObjectIdentifier(id + ".3"); - static final ASN1ObjectIdentifier countryName = new ASN1ObjectIdentifier(id + ".6"); - static final ASN1ObjectIdentifier localityName = new ASN1ObjectIdentifier(id + ".7"); - static final ASN1ObjectIdentifier stateOrProvinceName = new ASN1ObjectIdentifier(id + ".8"); - static final ASN1ObjectIdentifier organization = new ASN1ObjectIdentifier(id + ".10"); - static final ASN1ObjectIdentifier organizationalUnitName = new ASN1ObjectIdentifier(id + ".11"); + + /** 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"); - static final ASN1ObjectIdentifier id_at_name = new ASN1ObjectIdentifier(id + ".41"); + /** 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 } // + /** + * id-SHA1 OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } + * <p> + * 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)} - // + /** + * ripemd160 OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RIPEMD-160(1)} + * <p> + * 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) } - // + /** + * ripemd160WithRSAEncryption OBJECT IDENTIFIER ::= + * {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) } + * <p> + * 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 - static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7"); + /** 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 - // - static final ASN1ObjectIdentifier id_pe = new ASN1ObjectIdentifier(id_pkix + ".1"); + /** + * 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 - // + /** + * ISO ARC for standard certificate and CRL extensions + * <p> + * OID: 2.5.29 + */ static final ASN1ObjectIdentifier id_ce = new ASN1ObjectIdentifier("2.5.29"); - // - // authority information access - // - static final ASN1ObjectIdentifier id_ad = new ASN1ObjectIdentifier(id_pkix + ".48"); - static final ASN1ObjectIdentifier id_ad_caIssuers = new ASN1ObjectIdentifier(id_ad + ".2"); - static final ASN1ObjectIdentifier id_ad_ocsp = new ASN1ObjectIdentifier(id_ad + ".1"); + /** 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 and crl uri in AuthorityInformationAccess extension - // + /** OID for ocsp uri in AuthorityInformationAccess extension */ static final ASN1ObjectIdentifier ocspAccessMethod = id_ad_ocsp; - static final ASN1ObjectIdentifier crlAccessMethod = id_ad_caIssuers; + /** OID for crl uri in AuthorityInformationAccess extension */ + static final ASN1ObjectIdentifier crlAccessMethod = id_ad_caIssuers; } - diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/x509/package.html deleted file mode 100644 index 728921a..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and processing X.509 certificates. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java index 19ef12b..22db8cb 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java @@ -4,13 +4,8 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier; public interface ETSIQCObjectIdentifiers { - // - // base id - // - static final ASN1ObjectIdentifier id_etsi_qcs = new ASN1ObjectIdentifier("0.4.0.1862.1"); - - static final ASN1ObjectIdentifier id_etsi_qcs_QcCompliance = id_etsi_qcs.branch("1"); - static final ASN1ObjectIdentifier id_etsi_qcs_LimiteValue = id_etsi_qcs.branch("2"); - static final ASN1ObjectIdentifier id_etsi_qcs_RetentionPeriod = id_etsi_qcs.branch("3"); - static final ASN1ObjectIdentifier id_etsi_qcs_QcSSCD = id_etsi_qcs.branch("4"); + 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/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java index ecb5cce..0c840bd 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java @@ -4,11 +4,8 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier; public interface RFC3739QCObjectIdentifiers { - // - // base id - // - static final ASN1ObjectIdentifier id_qcs = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.11"); - - static final ASN1ObjectIdentifier id_qcs_pkixQCSyntax_v1 = id_qcs.branch("1"); - static final ASN1ObjectIdentifier id_qcs_pkixQCSyntax_v2 = id_qcs.branch("2"); + /** 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/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/package.html deleted file mode 100644 index 28cfef9..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and processing messages based around RFC3739 -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java index 8cac124..d338614 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java @@ -8,38 +8,53 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier; */ 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) + * <p> + * OID: 1.3.36.8.2 */ - public final static ASN1ObjectIdentifier id_sigi_kp = new ASN1ObjectIdentifier(id_sigi + ".2"); + public final static ASN1ObjectIdentifier id_sigi_kp = new ASN1ObjectIdentifier("1.3.36.8.2"); /** * Certificate policy IDs for German SigI (Signature Interoperability * Specification) + * <p> + * OID: 1.3.36.8.1 */ - public final static ASN1ObjectIdentifier id_sigi_cp = new ASN1ObjectIdentifier(id_sigi + ".1"); + public final static ASN1ObjectIdentifier id_sigi_cp = new ASN1ObjectIdentifier("1.3.36.8.1"); /** * Other Name IDs for German SigI (Signature Interoperability Specification) + * <p> + * OID: 1.3.36.8.4 */ - public final static ASN1ObjectIdentifier id_sigi_on = new ASN1ObjectIdentifier(id_sigi + ".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. + * <p> + * OID: 1.3.36.8.2.1 */ - public static final ASN1ObjectIdentifier id_sigi_kp_directoryService = new ASN1ObjectIdentifier(id_sigi_kp + ".1"); + public static final ASN1ObjectIdentifier id_sigi_kp_directoryService = new ASN1ObjectIdentifier("1.3.36.8.2.1"); /** * ID for PersonalData + * <p> + * OID: 1.3.36.8.4.1 */ - public static final ASN1ObjectIdentifier id_sigi_on_personalData = new ASN1ObjectIdentifier(id_sigi_on + ".1"); + public static final ASN1ObjectIdentifier id_sigi_on_personalData = new ASN1ObjectIdentifier("1.3.36.8.4.1"); /** - * Certificate is conform to german signature law. + * Certificate is conformant to german signature law. + * <p> + * OID: 1.3.36.8.1.1 */ - public static final ASN1ObjectIdentifier id_sigi_cp_sigconform = new ASN1ObjectIdentifier(id_sigi_cp + ".1"); + public static final ASN1ObjectIdentifier id_sigi_cp_sigconform = new ASN1ObjectIdentifier("1.3.36.8.1.1"); } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java index fb545c2..eeae0de 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java @@ -65,6 +65,8 @@ public class ECNamedCurveTable ecP = TeleTrusTNamedCurves.getByOID(oid); } + // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup + return ecP; } diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java index e059089..60f9008 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java @@ -39,11 +39,21 @@ public class X9ECParameters } X9Curve x9c = new X9Curve( - new X9FieldID((ASN1Sequence)seq.getObjectAt(1)), - (ASN1Sequence)seq.getObjectAt(2)); + X9FieldID.getInstance(seq.getObjectAt(1)), + ASN1Sequence.getInstance(seq.getObjectAt(2))); this.curve = x9c.getCurve(); - this.g = new X9ECPoint(curve, (ASN1OctetString)seq.getObjectAt(3)).getPoint(); + 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(); @@ -93,7 +103,7 @@ public class X9ECParameters byte[] seed) { this.curve = curve; - this.g = g; + this.g = g.normalize(); this.n = n; this.h = h; this.seed = seed; diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java index a4acb6e..cbb9116 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java @@ -18,7 +18,7 @@ public class X9ECPoint public X9ECPoint( ECPoint p) { - this.p = p; + this.p = p.normalize(); } public X9ECPoint( diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java index 30598e2..a210352 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java @@ -71,11 +71,26 @@ public class X9FieldID this.parameters = new DERSequence(fieldIdParams); } - public X9FieldID( + private X9FieldID( ASN1Sequence seq) { - this.id = (ASN1ObjectIdentifier)seq.getObjectAt(0); - this.parameters = (ASN1Primitive)seq.getObjectAt(1); + 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() diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java index f005cfa..eabf90e 100644 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java @@ -2,109 +2,172 @@ package org.bouncycastle.asn1.x9; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * + * X9.62 + * <pre> + * ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2) + * us(840) ansi-x962(10045) } + * </pre> + */ public interface X9ObjectIdentifiers { - // - // X9.62 - // - // ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2) - // us(840) ansi-x962(10045) } - // + /** 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"); - static final ASN1ObjectIdentifier ecdsa_with_SHA1 = new ASN1ObjectIdentifier(id_ecSigType + ".1"); + /** 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 - // + /** + * Named curves base + * <p> + * OID: 1.2.840.10045.1 + */ static final ASN1ObjectIdentifier ellipticCurve = ansi_X9_62.branch("3"); - // - // Two Curves - // + /** + * Two Curves + * <p> + * 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 - // + /** + * Prime Curves + * <p> + * 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 } + /** + * DSA + * <pre> + * dsapublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2) + * us(840) ansi-x957(10040) number-type(4) 1 } + * </pre> + * 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 } + * <pre> + * id-dsa-with-sha1 OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 } + * </pre> + * OID: 1.2.840.10040.4.3 */ - public static final ASN1ObjectIdentifier id_dsa_with_sha1 = new ASN1ObjectIdentifier("1.2.840.10040.4.3"); + static final ASN1ObjectIdentifier id_dsa_with_sha1 = new ASN1ObjectIdentifier("1.2.840.10040.4.3"); /** - * X9.63 + * X9.63 - Signature Specification + * <p> + * Base OID: 1.3.133.16.840.63.0 */ - public static final ASN1ObjectIdentifier x9_63_scheme = new ASN1ObjectIdentifier("1.3.133.16.840.63.0"); - public static final ASN1ObjectIdentifier dhSinglePass_stdDH_sha1kdf_scheme = x9_63_scheme.branch("2"); - public static final ASN1ObjectIdentifier dhSinglePass_cofactorDH_sha1kdf_scheme = x9_63_scheme.branch("3"); - public static final ASN1ObjectIdentifier mqvSinglePass_sha1kdf_scheme = x9_63_scheme.branch("16"); + 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 @@ -112,21 +175,33 @@ public interface X9ObjectIdentifiers 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 } - // - public static final ASN1ObjectIdentifier dhpublicnumber = ansi_X9_42.branch("2.1"); - - public static final ASN1ObjectIdentifier x9_42_schemes = ansi_X9_42.branch("3"); - public static final ASN1ObjectIdentifier dhStatic = x9_42_schemes.branch("1"); - public static final ASN1ObjectIdentifier dhEphem = x9_42_schemes.branch("2"); - public static final ASN1ObjectIdentifier dhOneFlow = x9_42_schemes.branch("3"); - public static final ASN1ObjectIdentifier dhHybrid1 = x9_42_schemes.branch("4"); - public static final ASN1ObjectIdentifier dhHybrid2 = x9_42_schemes.branch("5"); - public static final ASN1ObjectIdentifier dhHybridOneFlow = x9_42_schemes.branch("6"); - public static final ASN1ObjectIdentifier mqv2 = x9_42_schemes.branch("7"); - public static final ASN1ObjectIdentifier mqv1 = x9_42_schemes.branch("8"); + /** + * Diffie-Hellman + * <pre> + * dhpublicnumber OBJECT IDENTIFIER ::= { + * iso(1) member-body(2) us(840) ansi-x942(10046) number-type(2) 1 + * } + * </pre> + * 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/bcprov/src/main/java/org/bouncycastle/asn1/x9/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/x9/package.html deleted file mode 100644 index 42fc97c..0000000 --- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Support classes useful for encoding and supporting X9.62 elliptic curve. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java index bdb694d..dd056ac 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java @@ -54,7 +54,7 @@ public class BufferedBlockCipher } else { - partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx))); + 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))); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java index ef6e29e..0e2b4b0 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java @@ -7,11 +7,6 @@ public interface DerivationFunction { public void init(DerivationParameters param); - /** - * return the message digest used as the basis for the function - */ - public Digest getDigest(); - public int generateBytes(byte[] out, int outOff, int len) throws DataLengthException, IllegalArgumentException; } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java new file mode 100644 index 0000000..180382d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java @@ -0,0 +1,13 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java new file mode 100644 index 0000000..16198ba --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java @@ -0,0 +1,13 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java index 59944e0..a491b9d 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java @@ -42,10 +42,13 @@ public class ECDHBasicAgreement CipherParameters pubKey) { ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey; - ECPoint P = pub.getQ().multiply(key.getD()); + ECPoint P = pub.getQ().multiply(key.getD()).normalize(); - // if (p.isInfinity()) throw new RuntimeException("d*Q == infinity"); + if (P.isInfinity()) + { + throw new IllegalStateException("Infinity is not a valid agreement value for ECDH"); + } - return P.getX().toBigInteger(); + return P.getAffineXCoord().toBigInteger(); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java index 12b8405..28140df 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java @@ -47,12 +47,18 @@ public class ECDHCBasicAgreement public BigInteger calculateAgreement( CipherParameters pubKey) { - ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey; - ECDomainParameters params = pub.getParameters(); - ECPoint P = pub.getQ().multiply(params.getH().multiply(key.getD())); + ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey; + ECDomainParameters params = pub.getParameters(); - // if (p.isInfinity()) throw new RuntimeException("Invalid public key"); + BigInteger hd = params.getH().multiply(key.getD()).mod(params.getN()); - return P.getX().toBigInteger(); + 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/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java index da88b4a..794f555 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java @@ -11,6 +11,7 @@ import org.bouncycastle.crypto.params.MQVPrivateParameters; import org.bouncycastle.crypto.params.MQVPublicParameters; import org.bouncycastle.math.ec.ECAlgorithms; import org.bouncycastle.math.ec.ECConstants; +import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; public class ECMQVBasicAgreement @@ -37,9 +38,14 @@ public class ECMQVBasicAgreement ECPoint agreement = calculateMqvAgreement(staticPrivateKey.getParameters(), staticPrivateKey, privParams.getEphemeralPrivateKey(), privParams.getEphemeralPublicKey(), - pubParams.getStaticPublicKey(), pubParams.getEphemeralPublicKey()); + pubParams.getStaticPublicKey(), pubParams.getEphemeralPublicKey()).normalize(); - return agreement.getX().toBigInteger(); + 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 @@ -55,37 +61,31 @@ public class ECMQVBasicAgreement int e = (n.bitLength() + 1) / 2; BigInteger powE = ECConstants.ONE.shiftLeft(e); - // The Q2U public key is optional - ECPoint q; - if (Q2U == null) - { - q = parameters.getG().multiply(d2U.getD()); - } - else - { - q = Q2U.getQ(); - } + ECCurve curve = parameters.getCurve(); - BigInteger x = q.getX().toBigInteger(); + 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).mod(n).add(d2U.getD()).mod(n); + BigInteger s = d1U.getD().multiply(Q2UBar).add(d2U.getD()).mod(n); - BigInteger xPrime = Q2V.getQ().getX().toBigInteger(); + BigInteger xPrime = q2v.getAffineXCoord().toBigInteger(); BigInteger xPrimeBar = xPrime.mod(powE); BigInteger Q2VBar = xPrimeBar.setBit(e); BigInteger hs = parameters.getH().multiply(s).mod(n); -// ECPoint p = Q1V.getQ().multiply(Q2VBar).add(Q2V.getQ()).multiply(hs); - ECPoint p = ECAlgorithms.sumOfTwoMultiplies( - Q1V.getQ(), Q2VBar.multiply(hs).mod(n), Q2V.getQ(), hs); - - if (p.isInfinity()) - { - throw new IllegalStateException("Infinity is not a valid agreement value for MQV"); - } - - return p; + return ECAlgorithms.sumOfTwoMultiplies( + q1v, Q2VBar.multiply(hs).mod(n), q2v, hs); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html deleted file mode 100644 index db47144..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Password Authenticated Key Exchange by Juggling (J-PAKE). -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java index 6803953..500b1dd 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java @@ -11,9 +11,9 @@ import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.DerivationFunction; import org.bouncycastle.crypto.DerivationParameters; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.DigestDerivationFunction; import org.bouncycastle.crypto.generators.KDF2BytesGenerator; import org.bouncycastle.crypto.params.KDFParameters; import org.bouncycastle.crypto.util.Pack; @@ -22,9 +22,9 @@ import org.bouncycastle.crypto.util.Pack; * X9.63 based key derivation function for ECDH CMS. */ public class ECDHKEKGenerator - implements DerivationFunction + implements DigestDerivationFunction { - private DerivationFunction kdf; + private DigestDerivationFunction kdf; private ASN1ObjectIdentifier algorithm; private int keySize; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html deleted file mode 100644 index 4b49331..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Basic key agreement classes. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java new file mode 100644 index 0000000..3969fe8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java @@ -0,0 +1,93 @@ +package org.bouncycastle.crypto.commitments; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.Commitment; +import org.bouncycastle.crypto.Committer; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.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). + * <p> + * The algorithm used by this class differs from the one given in that it includes the length of the message in the hash calculation. + * </p> + */ +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/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java index 1494c3c..b5860b5 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java @@ -12,6 +12,9 @@ import org.bouncycastle.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). + * <p> + * Use this class if you can enforce fixed length for messages. If you need something more general, use the GeneralHashCommitter. + * </p> */ public class HashCommitter implements Committer @@ -55,7 +58,7 @@ public class HashCommitter } /** - * Return true if the passed in commitment represents a commitment to the passed in maessage. + * 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. @@ -63,6 +66,11 @@ public class HashCommitter */ 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); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html deleted file mode 100644 index 302cc60..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Commitment algorithms. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java new file mode 100644 index 0000000..55e579e --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java @@ -0,0 +1,333 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.util.Pack; +import org.bouncycastle.util.Memoable; + +/** + * Implementation of Chinese SM3 digest as described at + * http://tools.ietf.org/html/draft-shen-sm3-hash-00 + * and at .... ( Chinese PDF ) + * <p/> + * 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/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java new file mode 100644 index 0000000..06eaabd --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java @@ -0,0 +1,116 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.crypto.engines.ThreefishEngine; +import org.bouncycastle.crypto.params.SkeinParameters; +import org.bouncycastle.util.Memoable; + +/** + * Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes, + * based on the {@link ThreefishEngine Threefish} tweakable block cipher. + * <p/> + * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3 + * competition in October 2010. + * <p/> + * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + * <p/> + * + * @see SkeinEngine + * @see SkeinParameters + */ +public class SkeinDigest + implements ExtendedDigest, Memoable +{ + /** + * 256 bit block size - Skein-256 + */ + public static final int SKEIN_256 = SkeinEngine.SKEIN_256; + /** + * 512 bit block size - Skein-512 + */ + public static final int SKEIN_512 = SkeinEngine.SKEIN_512; + /** + * 1024 bit block size - Skein-1024 + */ + public static final int SKEIN_1024 = SkeinEngine.SKEIN_1024; + + private SkeinEngine engine; + + /** + * Constructs a Skein digest with an internal state size and output size. + * + * @param stateSizeBits the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or + * {@link #SKEIN_1024}. + * @param digestSizeBits the output/digest size to produce in bits, which must be an integral number of + * bytes. + */ + public SkeinDigest(int stateSizeBits, int digestSizeBits) + { + this.engine = new SkeinEngine(stateSizeBits, digestSizeBits); + init(null); + } + + public SkeinDigest(SkeinDigest digest) + { + this.engine = new SkeinEngine(digest.engine); + } + + public void reset(Memoable other) + { + SkeinDigest d = (SkeinDigest)other; + engine.reset(d.engine); + } + + public Memoable copy() + { + return new SkeinDigest(this); + } + + public String getAlgorithmName() + { + return "Skein-" + (engine.getBlockSize() * 8) + "-" + (engine.getOutputSize() * 8); + } + + public int getDigestSize() + { + return engine.getOutputSize(); + } + + public int getByteLength() + { + return engine.getBlockSize(); + } + + /** + * Optionally initialises the Skein digest with the provided parameters.<br> + * See {@link SkeinParameters} for details on the parameterisation of the Skein hash function. + * + * @param params the parameters to apply to this engine, or <code>null</code> to use no parameters. + */ + public void init(SkeinParameters params) + { + engine.init(params); + } + + public void reset() + { + engine.reset(); + } + + public void update(byte in) + { + engine.update(in); + } + + public void update(byte[] in, int inOff, int len) + { + engine.update(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + return engine.doFinal(out, outOff); + } + +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java new file mode 100644 index 0000000..bca524e --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java @@ -0,0 +1,817 @@ +package org.bouncycastle.crypto.digests; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.engines.ThreefishEngine; +import org.bouncycastle.crypto.macs.SkeinMac; +import org.bouncycastle.crypto.params.SkeinParameters; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Memoable; + +/** + * Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block + * sizes, based on the {@link ThreefishEngine Threefish} tweakable block cipher. + * <p/> + * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3 + * competition in October 2010. + * <p/> + * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + * <p/> + * This implementation is the basis for {@link SkeinDigest} and {@link SkeinMac}, implementing the + * parameter based configuration system that allows Skein to be adapted to multiple applications. <br> + * Initialising the engine with {@link SkeinParameters} allows standard and arbitrary parameters to + * be applied during the Skein hash function. + * <p/> + * Implemented: + * <ul> + * <li>256, 512 and 1024 bit internal states.</li> + * <li>Full 96 bit input length.</li> + * <li>Parameters defined in the Skein specification, and arbitrary other pre and post message + * parameters.</li> + * <li>Arbitrary output size in 1 byte intervals.</li> + * </ul> + * <p/> + * Not implemented: + * <ul> + * <li>Sub-byte length input (bit padding).</li> + * <li>Tree hashing.</li> + * </ul> + * + * @see SkeinParameters + */ +public class SkeinEngine + implements Memoable +{ + /** + * 256 bit block size - Skein 256 + */ + public static final int SKEIN_256 = ThreefishEngine.BLOCKSIZE_256; + /** + * 512 bit block size - Skein 512 + */ + public static final int SKEIN_512 = ThreefishEngine.BLOCKSIZE_512; + /** + * 1024 bit block size - Skein 1024 + */ + public static final int SKEIN_1024 = ThreefishEngine.BLOCKSIZE_1024; + + // Minimal at present, but more complex when tree hashing is implemented + private static class Configuration + { + private byte[] bytes = new byte[32]; + + public Configuration(long outputSizeBits) + { + // 0..3 = ASCII SHA3 + bytes[0] = (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 + */ + final ThreefishEngine threefish; + + /** + * Size of the digest output, in bytes + */ + private final int outputSizeBytes; + + /** + * The current chaining/state value + */ + long[] chain; + + /** + * The initial state value + */ + private long[] initialState; + + /** + * The (optional) key parameter + */ + private byte[] key; + + /** + * Parameters to apply prior to the message + */ + private Parameter[] preMessageParameters; + + /** + * Parameters to apply after the message, but prior to output + */ + private Parameter[] postMessageParameters; + + /** + * The current UBI operation + */ + private final UBI ubi; + + /** + * Buffer for single byte update method + */ + private final byte[] singleByte = new byte[1]; + + /** + * Constructs a Skein engine. + * + * @param 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 <code>null</code> to use no parameters. + */ + public void init(SkeinParameters params) + { + this.chain = null; + this.key = null; + this.preMessageParameters = null; + this.postMessageParameters = null; + + if (params != null) + { + byte[] key = params.getKey(); + if (key.length < 16) + { + throw new IllegalArgumentException("Skein key must be at least 128 bits."); + } + initParams(params.getParameters()); + } + createInitialState(); + + // Initialise message block + ubiInit(PARAM_TYPE_MESSAGE); + } + + private void initParams(Hashtable parameters) + { + Enumeration keys = parameters.keys(); + final Vector pre = new Vector(); + final Vector post = new Vector(); + + while (keys.hasMoreElements()) + { + Integer type = (Integer)keys.nextElement(); + byte[] value = (byte[])parameters.get(type); + + if (type.intValue() == PARAM_TYPE_KEY) + { + this.key = value; + } + else if (type.intValue() < PARAM_TYPE_MESSAGE) + { + pre.addElement(new Parameter(type.intValue(), value)); + } + else + { + post.addElement(new Parameter(type.intValue(), value)); + } + } + preMessageParameters = new Parameter[pre.size()]; + pre.copyInto(preMessageParameters); + sort(preMessageParameters); + + postMessageParameters = new Parameter[post.size()]; + post.copyInto(postMessageParameters); + sort(postMessageParameters); + } + + private static void sort(Parameter[] params) + { + if (params == null) + { + return; + } + // Insertion sort, for Java 1.1 compatibility + for (int i = 1; i < params.length; i++) + { + Parameter param = params[i]; + int hole = i; + while (hole > 0 && param.getType() < params[hole - 1].getType()) + { + params[hole] = params[hole - 1]; + hole = hole - 1; + } + params[hole] = param; + } + } + + /** + * Calculate the initial (pre message block) chaining state. + */ + private void createInitialState() + { + long[] precalc = (long[])INITIAL_STATES.get(variantIdentifier(getBlockSize(), getOutputSize())); + if ((key == null) && (precalc != null)) + { + // Precalculated UBI(CFG) + chain = Arrays.clone(precalc); + } + else + { + // Blank initial state + chain = new long[getBlockSize() / 8]; + + // Process key block + if (key != null) + { + ubiComplete(SkeinParameters.PARAM_TYPE_KEY, key); + } + + // Process configuration block + ubiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).getBytes()); + } + + // Process additional pre-message parameters + if (preMessageParameters != null) + { + for (int i = 0; i < preMessageParameters.length; i++) + { + Parameter param = preMessageParameters[i]; + ubiComplete(param.getType(), param.getValue()); + } + } + initialState = Arrays.clone(chain); + } + + /** + * Reset the engine to the initial state (with the key and any pre-message parameters , ready to + * accept message input. + */ + public void reset() + { + System.arraycopy(initialState, 0, chain, 0, chain.length); + + ubiInit(PARAM_TYPE_MESSAGE); + } + + private void ubiComplete(int type, byte[] value) + { + ubiInit(type); + this.ubi.update(value, 0, value.length, chain); + ubiFinal(); + } + + private void ubiInit(int type) + { + this.ubi.reset(type); + } + + private void ubiFinal() + { + ubi.doFinal(chain); + } + + private void checkInitialised() + { + if (this.ubi == null) + { + throw new IllegalArgumentException("Skein engine is not initialised."); + } + } + + public void update(byte in) + { + singleByte[0] = in; + update(singleByte, 0, 1); + } + + public void update(byte[] in, int inOff, int len) + { + checkInitialised(); + ubi.update(in, inOff, len, chain); + } + + public int doFinal(byte[] out, int outOff) + { + checkInitialised(); + if (out.length < (outOff + outputSizeBytes)) + { + throw new DataLengthException("Output buffer is too short to hold output of " + outputSizeBytes + " bytes"); + } + + // Finalise message block + ubiFinal(); + + // Process additional post-message parameters + if (postMessageParameters != null) + { + for (int i = 0; i < postMessageParameters.length; i++) + { + Parameter param = postMessageParameters[i]; + ubiComplete(param.getType(), param.getValue()); + } + } + + // Perform the output transform + final int blockSize = getBlockSize(); + final int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize); + for (int i = 0; i < blocksRequired; i++) + { + final int toWrite = Math.min(blockSize, outputSizeBytes - (i * blockSize)); + output(i, out, outOff + (i * blockSize), toWrite); + } + + reset(); + + return outputSizeBytes; + } + + private void output(long outputSequence, byte[] out, int outOff, int outputBytes) + { + byte[] currentBytes = new byte[8]; + ThreefishEngine.wordToBytes(outputSequence, currentBytes, 0); + + // Output is a sequence of UBI invocations all of which use and preserve the pre-output + // state + long[] outputWords = new long[chain.length]; + ubiInit(PARAM_TYPE_OUTPUT); + this.ubi.update(currentBytes, 0, currentBytes.length, outputWords); + ubi.doFinal(outputWords); + + final int wordsRequired = ((outputBytes + 8 - 1) / 8); + for (int i = 0; i < wordsRequired; i++) + { + int toWrite = Math.min(8, outputBytes - (i * 8)); + if (toWrite == 8) + { + ThreefishEngine.wordToBytes(outputWords[i], out, outOff + (i * 8)); + } + else + { + ThreefishEngine.wordToBytes(outputWords[i], currentBytes, 0); + System.arraycopy(currentBytes, 0, out, outOff + (i * 8), toWrite); + } + } + } + +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html deleted file mode 100644 index 0a0d95c..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Message digest classes. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java index c8c548e..19c0beb 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java @@ -43,6 +43,6 @@ public class ECElGamalDecryptor ECPoint tmp = pair.getX().multiply(key.getD()); - return pair.getY().add(tmp.negate()); + return pair.getY().add(tmp.negate()).normalize(); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java index e5569a8..2a0b78d 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java @@ -6,7 +6,6 @@ import java.security.SecureRandom; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; -import org.bouncycastle.math.ec.ECConstants; import org.bouncycastle.math.ec.ECPoint; /** @@ -69,6 +68,6 @@ public class ECElGamalEncryptor ECPoint gamma = g.multiply(k); ECPoint phi = key.getQ().multiply(k).add(point); - return new ECPair(gamma, phi); + return new ECPair(gamma.normalize(), phi.normalize()); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java new file mode 100644 index 0000000..e35e077 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java @@ -0,0 +1,71 @@ +package org.bouncycastle.crypto.ec; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java index 32ba070..74016c1 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java @@ -69,6 +69,6 @@ public class ECNewPublicKeyTransform ECPoint gamma = g.multiply(k); ECPoint phi = key.getQ().multiply(k).add(cipherText.getY()); - return new ECPair(gamma, phi); + return new ECPair(gamma.normalize(), phi.normalize()); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java index b037984..b293759 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java @@ -12,11 +12,13 @@ import org.bouncycastle.math.ec.ECPoint; * this transforms the original randomness used for an ElGamal encryption. */ public class ECNewRandomnessTransform - implements ECPairTransform + implements ECPairFactorTransform { private ECPublicKeyParameters key; private SecureRandom random; + private BigInteger lastK; + /** * initialise the underlying EC ElGamal engine. * @@ -71,6 +73,18 @@ public class ECNewRandomnessTransform ECPoint gamma = g.multiply(k); ECPoint phi = key.getQ().multiply(k).add(cipherText.getY()); - return new ECPair(cipherText.getX().add(gamma), phi); + 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/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java index d910f3c..ea3b4b9 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java @@ -23,16 +23,18 @@ public class ECPair return y; } - public byte[] getEncoded() + public boolean equals(ECPair other) { - byte[] xEnc = x.getEncoded(); - byte[] yEnc = y.getEncoded(); - - byte[] full = new byte[xEnc.length + yEnc.length]; + return other.getX().equals(getX()) && other.getY().equals(getY()); + } - System.arraycopy(xEnc, 0, full, 0, xEnc.length); - System.arraycopy(yEnc, 0, full, xEnc.length, yEnc.length); + public boolean equals(Object other) + { + return other instanceof ECPair ? equals((ECPair)other) : false; + } - return full; + public int hashCode() + { + return x.hashCode() + 37 * y.hashCode(); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java new file mode 100644 index 0000000..be48551 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java @@ -0,0 +1,14 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html deleted file mode 100644 index d50edcf..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Lightweight EC point operations, such as EC ElGamal and randomness transforms. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html deleted file mode 100644 index fc56f63..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Block encodings for asymmetric ciphers. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java new file mode 100644 index 0000000..2d1de39 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java @@ -0,0 +1,186 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java index 6b3da1c..89271f0 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java @@ -89,8 +89,7 @@ public class Grain128Engine System.arraycopy(iv, 0, workingIV, 0, iv.length); System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length); - setKey(workingKey, workingIV); - initGrain(); + reset(); } /** diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java index c3baaec..782a93c 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java @@ -89,8 +89,7 @@ public class Grainv1Engine System.arraycopy(iv, 0, workingIV, 0, iv.length); System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length); - setKey(workingKey, workingIV); - initGrain(); + reset(); } /** diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java index 69da0f0..015e49e 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java @@ -118,6 +118,7 @@ public class HC128Engine "The key must be 128 bits long"); } + idx = 0; cnt = 0; int[] w = new int[1280]; @@ -246,7 +247,6 @@ public class HC128Engine public void reset() { - idx = 0; init(); } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java index 538d244..8bd7e9d 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java @@ -99,6 +99,7 @@ public class HC256Engine iv = newIV; } + idx = 0; cnt = 0; int[] w = new int[2560]; @@ -226,7 +227,6 @@ public class HC256Engine public void reset() { - idx = 0; init(); } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java index 95a395a..1ad2c9d 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java @@ -12,14 +12,25 @@ import org.bouncycastle.crypto.OutputLengthException; public class NullEngine implements BlockCipher { private boolean initialised; - protected static final int BLOCK_SIZE = 1; - + protected static final int DEFAULT_BLOCK_SIZE = 1; + private final int blockSize; + /** - * Standard constructor. + * Constructs a null engine with a block size of 1 byte. */ public NullEngine() { - super(); + 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) @@ -44,7 +55,7 @@ public class NullEngine implements BlockCipher */ public int getBlockSize() { - return BLOCK_SIZE; + return blockSize; } /* (non-Javadoc) @@ -57,22 +68,22 @@ public class NullEngine implements BlockCipher { throw new IllegalStateException("Null engine not initialised"); } - if ((inOff + BLOCK_SIZE) > in.length) - { - throw new DataLengthException("input buffer too short"); - } + 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]; + } - if ((outOff + BLOCK_SIZE) > out.length) - { - throw new OutputLengthException("output buffer too short"); - } - - for (int i = 0; i < BLOCK_SIZE; ++i) - { - out[outOff + i] = in[inOff + i]; - } - - return BLOCK_SIZE; + return blockSize; } /* (non-Javadoc) diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java index 540bd25..cfd86fb 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java @@ -85,7 +85,7 @@ public class RFC3394WrapEngine byte[] buf = new byte[8 + iv.length]; System.arraycopy(iv, 0, block, 0, iv.length); - System.arraycopy(in, 0, block, iv.length, inLen); + System.arraycopy(in, inOff, block, iv.length, inLen); engine.init(true, param); @@ -137,8 +137,8 @@ public class RFC3394WrapEngine byte[] a = new byte[iv.length]; byte[] buf = new byte[8 + iv.length]; - System.arraycopy(in, 0, a, 0, iv.length); - System.arraycopy(in, iv.length, block, 0, inLen - 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); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java index e7fb943..c9765bf 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java @@ -1,5 +1,8 @@ package org.bouncycastle.crypto.engines; +import java.math.BigInteger; +import java.security.SecureRandom; + import org.bouncycastle.crypto.AsymmetricBlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; @@ -8,16 +11,13 @@ import org.bouncycastle.crypto.params.RSAKeyParameters; import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; import org.bouncycastle.util.BigIntegers; -import java.math.BigInteger; -import java.security.SecureRandom; - /** * this does your basic RSA algorithm with blinding */ public class RSABlindedEngine implements AsymmetricBlockCipher { - private static BigInteger ONE = BigInteger.valueOf(1); + private static final BigInteger ONE = BigInteger.valueOf(1); private RSACoreEngine core = new RSACoreEngine(); private RSAKeyParameters key; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java index 6d4210d..2d6140d 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java @@ -13,27 +13,28 @@ import org.bouncycastle.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 - private final static byte[] + 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; - private int[] engineState = new int[STATE_SIZE]; // state - private int[] x = new int[STATE_SIZE] ; // internal buffer - private byte[] keyStream = new byte[STATE_SIZE * 4], // expanded state, 64 bytes - workingKey = null, - workingIV = null; + 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; /* @@ -42,6 +43,28 @@ public class Salsa20Engine 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. @@ -61,34 +84,43 @@ public class Salsa20Engine if (!(params instanceof ParametersWithIV)) { - throw new IllegalArgumentException("Salsa20 Init parameters must include an IV"); + throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include an IV"); } ParametersWithIV ivParams = (ParametersWithIV) params; byte[] iv = ivParams.getIV(); - - if (iv == null || iv.length != 8) + if (iv == null || iv.length != getNonceSize()) { - throw new IllegalArgumentException("Salsa20 requires exactly 8 bytes of IV"); + throw new IllegalArgumentException(getAlgorithmName() + " requires exactly " + getNonceSize() + + " bytes of IV"); } if (!(ivParams.getParameters() instanceof KeyParameter)) { - throw new IllegalArgumentException("Salsa20 Init parameters must include a key"); + throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include a key"); } KeyParameter key = (KeyParameter) ivParams.getParameters(); - workingKey = key.getKey(); - workingIV = iv; + setKey(key.getKey(), iv); + reset(); + initialised = true; + } - setKey(workingKey, workingIV); + protected int getNonceSize() + { + return 8; } public String getAlgorithmName() { - return "Salsa20"; + String name = "Salsa20"; + if (rounds != DEFAULT_ROUNDS) + { + name += "/" + rounds; + } + return name; } public byte returnByte(byte in) @@ -101,11 +133,7 @@ public class Salsa20Engine if (index == 0) { generateKeyStream(keyStream); - - if (++engineState[8] == 0) - { - ++engineState[9]; - } + advanceCounter(); } byte out = (byte)(keyStream[index]^in); @@ -114,6 +142,14 @@ public class Salsa20Engine return out; } + protected void advanceCounter() + { + if (++engineState[8] == 0) + { + ++engineState[9]; + } + } + public void processBytes( byte[] in, int inOff, @@ -123,7 +159,7 @@ public class Salsa20Engine { if (!initialised) { - throw new IllegalStateException(getAlgorithmName()+" not initialised"); + throw new IllegalStateException(getAlgorithmName() + " not initialised"); } if ((inOff + len) > in.length) @@ -146,11 +182,7 @@ public class Salsa20Engine if (index == 0) { generateKeyStream(keyStream); - - if (++engineState[8] == 0) - { - ++engineState[9]; - } + advanceCounter(); } out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]); @@ -160,28 +192,32 @@ public class Salsa20Engine public void reset() { - setKey(workingKey, workingIV); + index = 0; + resetLimitCounter(); + resetCounter(); } - // Private implementation - - private void setKey(byte[] keyBytes, byte[] ivBytes) + protected void resetCounter() { - workingKey = keyBytes; - workingIV = ivBytes; + engineState[8] = engineState[9] = 0; + } - index = 0; - resetCounter(); + 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(workingKey, 0); - engineState[2] = Pack.littleEndianToInt(workingKey, 4); - engineState[3] = Pack.littleEndianToInt(workingKey, 8); - engineState[4] = Pack.littleEndianToInt(workingKey, 12); + 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 (workingKey.length == 32) + if (keyBytes.length == 32) { constants = sigma; offset = 16; @@ -191,26 +227,25 @@ public class Salsa20Engine constants = tau; } - engineState[11] = Pack.littleEndianToInt(workingKey, offset); - engineState[12] = Pack.littleEndianToInt(workingKey, offset+4); - engineState[13] = Pack.littleEndianToInt(workingKey, offset+8); - engineState[14] = Pack.littleEndianToInt(workingKey, offset+12); + 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(workingIV, 0); - engineState[7] = Pack.littleEndianToInt(workingIV, 4); - engineState[8] = engineState[9] = 0; - - initialised = true; + engineState[6] = Pack.littleEndianToInt(ivBytes, 0); + engineState[7] = Pack.littleEndianToInt(ivBytes, 4); + resetCounter(); } - private void generateKeyStream(byte[] output) + protected void generateKeyStream(byte[] output) { - salsaCore(20, engineState, x); + salsaCore(rounds, engineState, x); Pack.intToLittleEndian(x, output, 0); } @@ -223,50 +258,86 @@ public class Salsa20Engine */ public static void salsaCore(int rounds, int[] input, int[] x) { - // TODO Exception if rounds odd? + 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"); + } - System.arraycopy(input, 0, x, 0, input.length); + 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) { - x[ 4] ^= rotl((x[ 0]+x[12]), 7); - x[ 8] ^= rotl((x[ 4]+x[ 0]), 9); - x[12] ^= rotl((x[ 8]+x[ 4]),13); - x[ 0] ^= rotl((x[12]+x[ 8]),18); - x[ 9] ^= rotl((x[ 5]+x[ 1]), 7); - x[13] ^= rotl((x[ 9]+x[ 5]), 9); - x[ 1] ^= rotl((x[13]+x[ 9]),13); - x[ 5] ^= rotl((x[ 1]+x[13]),18); - x[14] ^= rotl((x[10]+x[ 6]), 7); - x[ 2] ^= rotl((x[14]+x[10]), 9); - x[ 6] ^= rotl((x[ 2]+x[14]),13); - x[10] ^= rotl((x[ 6]+x[ 2]),18); - x[ 3] ^= rotl((x[15]+x[11]), 7); - x[ 7] ^= rotl((x[ 3]+x[15]), 9); - x[11] ^= rotl((x[ 7]+x[ 3]),13); - x[15] ^= rotl((x[11]+x[ 7]),18); - x[ 1] ^= rotl((x[ 0]+x[ 3]), 7); - x[ 2] ^= rotl((x[ 1]+x[ 0]), 9); - x[ 3] ^= rotl((x[ 2]+x[ 1]),13); - x[ 0] ^= rotl((x[ 3]+x[ 2]),18); - x[ 6] ^= rotl((x[ 5]+x[ 4]), 7); - x[ 7] ^= rotl((x[ 6]+x[ 5]), 9); - x[ 4] ^= rotl((x[ 7]+x[ 6]),13); - x[ 5] ^= rotl((x[ 4]+x[ 7]),18); - x[11] ^= rotl((x[10]+x[ 9]), 7); - x[ 8] ^= rotl((x[11]+x[10]), 9); - x[ 9] ^= rotl((x[ 8]+x[11]),13); - x[10] ^= rotl((x[ 9]+x[ 8]),18); - x[12] ^= rotl((x[15]+x[14]), 7); - x[13] ^= rotl((x[12]+x[15]), 9); - x[14] ^= rotl((x[13]+x[12]),13); - x[15] ^= rotl((x[14]+x[13]),18); + 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); } - for (int i = 0; i < STATE_SIZE; ++i) - { - x[i] += input[i]; - } + 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]; } /** @@ -277,12 +348,12 @@ public class Salsa20Engine * * @return rotated x */ - private static int rotl(int x, int y) + protected static int rotl(int x, int y) { return (x << y) | (x >>> -y); } - private void resetCounter() + private void resetLimitCounter() { cW0 = 0; cW1 = 0; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java new file mode 100644 index 0000000..b59205d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java @@ -0,0 +1,201 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.crypto.params.KeyParameter; + +/** + * 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. + * <p> + * 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 + * </p> + */ +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/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java index b09f189..ac65443 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java @@ -105,6 +105,11 @@ public class TEAEngine 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); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java new file mode 100644 index 0000000..74bccfe --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java @@ -0,0 +1,1494 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.TweakableBlockCipherParameters; + +/** + * Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block + * sizes. + * <p/> + * This is the 1.3 version of Threefish defined in the Skein hash function submission to the NIST + * SHA-3 competition in October 2010. + * <p/> + * Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + * <p/> + * This implementation inlines all round functions, unrolls 8 rounds, and uses 1.2k of static tables + * to speed up key schedule injection. <br> + * 2 x block size state is retained by each cipher instance. + */ +public class ThreefishEngine + implements BlockCipher +{ + /** + * 256 bit block size - Threefish-256 + */ + public static final int BLOCKSIZE_256 = 256; + /** + * 512 bit block size - Threefish-512 + */ + public static final int BLOCKSIZE_512 = 512; + /** + * 1024 bit block size - Threefish-1024 + */ + public static final int BLOCKSIZE_1024 = 1024; + + /** + * Size of the tweak in bytes (always 128 bit/16 bytes) + */ + private static final int TWEAK_SIZE_BYTES = 16; + private static final int TWEAK_SIZE_WORDS = TWEAK_SIZE_BYTES / 8; + + /** + * Rounds in Threefish-256 + */ + private static final int ROUNDS_256 = 72; + /** + * Rounds in Threefish-512 + */ + private static final int ROUNDS_512 = 72; + /** + * Rounds in Threefish-1024 + */ + private static final int ROUNDS_1024 = 80; + + /** + * Max rounds of any of the variants + */ + private static final int MAX_ROUNDS = ROUNDS_1024; + + /** + * Key schedule parity constant + */ + private static final long C_240 = 0x1BD11BDAA9FC1A22L; + + /* Pre-calculated modulo arithmetic tables for key schedule lookups */ + private static int[] MOD9 = new int[MAX_ROUNDS]; + private static int[] MOD17 = new int[MOD9.length]; + private static int[] MOD5 = new int[MOD9.length]; + private static int[] MOD3 = new int[MOD9.length]; + + static + { + for (int i = 0; i < MOD9.length; i++) + { + MOD17[i] = i % 17; + MOD9[i] = i % 9; + MOD5[i] = i % 5; + MOD3[i] = i % 3; + } + } + + /** + * Block size in bytes + */ + private int blocksizeBytes; + + /** + * Block size in 64 bit words + */ + private int blocksizeWords; + + /** + * Buffer for byte oriented processBytes to call internal word API + */ + private long[] currentBlock; + + /** + * Tweak bytes (2 byte t1,t2, calculated t3 and repeat of t1,t2 for modulo free lookup + */ + private long[] t = new long[5]; + + /** + * Key schedule words + */ + private long[] kw; + + /** + * The internal cipher implementation (varies by blocksize) + */ + private ThreefishCipher cipher; + + private boolean forEncryption; + + /** + * Constructs a new Threefish cipher, with a specified block size. + * + * @param blocksizeBits the block size in bits, one of {@link #BLOCKSIZE_256}, {@link #BLOCKSIZE_512}, + * {@link #BLOCKSIZE_1024}. + */ + public ThreefishEngine(final int blocksizeBits) + { + this.blocksizeBytes = (blocksizeBits / 8); + this.blocksizeWords = (this.blocksizeBytes / 8); + this.currentBlock = new long[blocksizeWords]; + + /* + * Provide room for original key words, extended key word and repeat of key words for modulo + * free lookup of key schedule words. + */ + this.kw = new long[2 * blocksizeWords + 1]; + + switch (blocksizeBits) + { + case BLOCKSIZE_256: + cipher = new Threefish256Cipher(kw, t); + break; + case BLOCKSIZE_512: + cipher = new Threefish512Cipher(kw, t); + break; + case BLOCKSIZE_1024: + cipher = new Threefish1024Cipher(kw, t); + break; + default: + throw new IllegalArgumentException( + "Invalid blocksize - Threefish is defined with block size of 256, 512, or 1024 bits"); + } + } + + /** + * Initialise the engine. + * + * @param params an instance of {@link TweakableBlockCipherParameters}, or {@link KeyParameter} (to + * use a 0 tweak) + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + final byte[] keyBytes; + final byte[] tweakBytes; + + if (params instanceof TweakableBlockCipherParameters) + { + TweakableBlockCipherParameters tParams = (TweakableBlockCipherParameters)params; + keyBytes = tParams.getKey().getKey(); + tweakBytes = tParams.getTweak(); + } + else if (params instanceof KeyParameter) + { + keyBytes = ((KeyParameter)params).getKey(); + tweakBytes = null; + } + else + { + throw new IllegalArgumentException("Invalid parameter passed to Threefish init - " + + params.getClass().getName()); + } + + long[] keyWords = null; + long[] tweakWords = null; + + if (keyBytes != null) + { + if (keyBytes.length != this.blocksizeBytes) + { + throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeBytes + + " bytes)"); + } + keyWords = new long[blocksizeWords]; + for (int i = 0; i < keyWords.length; i++) + { + keyWords[i] = bytesToWord(keyBytes, i * 8); + } + } + if (tweakBytes != null) + { + if (tweakBytes.length != TWEAK_SIZE_BYTES) + { + throw new IllegalArgumentException("Threefish tweak must be " + TWEAK_SIZE_BYTES + " bytes"); + } + tweakWords = new long[]{bytesToWord(tweakBytes, 0), bytesToWord(tweakBytes, 8)}; + } + init(forEncryption, keyWords, tweakWords); + } + + /** + * Initialise the engine, specifying the key and tweak directly. + * + * @param forEncryption the cipher mode. + * @param key the words of the key, or <code>null</code> to use the current key. + * @param tweak the 2 word (128 bit) tweak, or <code>null</code> to use the current tweak. + */ + public void init(boolean forEncryption, final long[] key, final long[] tweak) + { + this.forEncryption = forEncryption; + if (key != null) + { + setKey(key); + } + if (tweak != null) + { + setTweak(tweak); + } + } + + private void setKey(long[] key) + { + if (key.length != this.blocksizeWords) + { + throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeWords + + " words)"); + } + + /* + * Full subkey schedule is deferred to execution to avoid per cipher overhead (10k for 512, + * 20k for 1024). + * + * Key and tweak word sequences are repeated, and static MOD17/MOD9/MOD5/MOD3 calculations + * used, to avoid expensive mod computations during cipher operation. + */ + + long knw = C_240; + for (int i = 0; i < blocksizeWords; i++) + { + kw[i] = key[i]; + knw = knw ^ kw[i]; + } + kw[blocksizeWords] = knw; + System.arraycopy(kw, 0, kw, blocksizeWords + 1, blocksizeWords); + } + + private void setTweak(long[] tweak) + { + if (tweak.length != TWEAK_SIZE_WORDS) + { + throw new IllegalArgumentException("Tweak must be " + TWEAK_SIZE_WORDS + " words."); + } + + /* + * Tweak schedule partially repeated to avoid mod computations during cipher operation + */ + t[0] = tweak[0]; + t[1] = tweak[1]; + t[2] = t[0] ^ t[1]; + t[3] = t[0]; + t[4] = t[1]; + } + + public String getAlgorithmName() + { + return "Threefish-" + (blocksizeBytes * 8); + } + + public int getBlockSize() + { + return blocksizeBytes; + } + + public void reset() + { + } + + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, + IllegalStateException + { + if ((outOff + blocksizeBytes) > out.length) + { + throw new DataLengthException("Output buffer too short"); + } + + if ((inOff + blocksizeBytes) > in.length) + { + throw new DataLengthException("Input buffer too short"); + } + + for (int i = 0; i < blocksizeBytes; i += 8) + { + currentBlock[i >> 3] = bytesToWord(in, inOff + i); + } + processBlock(this.currentBlock, this.currentBlock); + for (int i = 0; i < blocksizeBytes; i += 8) + { + wordToBytes(this.currentBlock[i >> 3], out, outOff + i); + } + + return blocksizeBytes; + } + + /** + * Process a block of data represented as 64 bit words. + * + * @param in a block sized buffer of words to process. + * @param out a block sized buffer of words to receive the output of the operation. + * @return the number of 8 byte words processed (which will be the same as the block size). + * @throws DataLengthException if either the input or output is not block sized. + * @throws IllegalStateException if this engine is not initialised. + */ + public int processBlock(long[] in, long[] out) + throws DataLengthException, IllegalStateException + { + if (kw[blocksizeWords] == 0) + { + throw new IllegalStateException("Threefish engine not initialised"); + } + + if (in.length != blocksizeWords) + { + throw new DataLengthException("Input buffer too short"); + } + if (out.length != blocksizeWords) + { + throw new DataLengthException("Output buffer too short"); + } + + if (forEncryption) + { + cipher.encryptBlock(in, out); + } + else + { + cipher.decryptBlock(in, out); + } + + return blocksizeWords; + } + + /** + * Read a single 64 bit word from input in LSB first order. + */ + // At least package protected for efficient access from inner class + public static long bytesToWord(final byte[] bytes, final int off) + { + if ((off + 8) > bytes.length) + { + // Help the JIT avoid index checks + throw new IllegalArgumentException(); + } + + long word = 0; + int index = off; + + word = (bytes[index++] & 0xffL); + word |= (bytes[index++] & 0xffL) << 8; + word |= (bytes[index++] & 0xffL) << 16; + word |= (bytes[index++] & 0xffL) << 24; + word |= (bytes[index++] & 0xffL) << 32; + word |= (bytes[index++] & 0xffL) << 40; + word |= (bytes[index++] & 0xffL) << 48; + word |= (bytes[index++] & 0xffL) << 56; + + return word; + } + + /** + * Write a 64 bit word to output in LSB first order. + */ + // At least package protected for efficient access from inner class + public static void wordToBytes(final long word, final byte[] bytes, final int off) + { + if ((off + 8) > bytes.length) + { + // Help the JIT avoid index checks + throw new IllegalArgumentException(); + } + int index = off; + + bytes[index++] = (byte)word; + bytes[index++] = (byte)(word >> 8); + bytes[index++] = (byte)(word >> 16); + bytes[index++] = (byte)(word >> 24); + bytes[index++] = (byte)(word >> 32); + bytes[index++] = (byte)(word >> 40); + bytes[index++] = (byte)(word >> 48); + bytes[index++] = (byte)(word >> 56); + } + + /** + * Rotate left + xor part of the mix operation. + */ + // Package protected for efficient access from inner class + static long rotlXor(long x, int n, long xor) + { + return ((x << n) | (x >>> -n)) ^ xor; + } + + /** + * Rotate xor + rotate right part of the unmix operation. + */ + // Package protected for efficient access from inner class + static long xorRotr(long x, int n, long xor) + { + long xored = x ^ xor; + return (xored >>> n) | (xored << -n); + } + + private static abstract class ThreefishCipher + { + /** + * The extended + repeated tweak words + */ + protected final long[] t; + /** + * The extended + repeated key words + */ + protected final long[] kw; + + protected ThreefishCipher(final long[] kw, final long[] t) + { + this.kw = kw; + this.t = t; + } + + abstract void encryptBlock(long[] block, long[] out); + + abstract void decryptBlock(long[] block, long[] out); + + } + + private static final class Threefish256Cipher + extends ThreefishCipher + { + /** + * Mix rotation constants defined in Skein 1.3 specification + */ + private static final int ROTATION_0_0 = 14, ROTATION_0_1 = 16; + private static final int ROTATION_1_0 = 52, ROTATION_1_1 = 57; + private static final int ROTATION_2_0 = 23, ROTATION_2_1 = 40; + private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 37; + + private static final int ROTATION_4_0 = 25, ROTATION_4_1 = 33; + private static final int ROTATION_5_0 = 46, ROTATION_5_1 = 12; + private static final int ROTATION_6_0 = 58, ROTATION_6_1 = 22; + private static final int ROTATION_7_0 = 32, ROTATION_7_1 = 32; + + public Threefish256Cipher(long[] kw, long[] t) + { + super(kw, t); + } + + void encryptBlock(long[] block, long[] out) + { + final long[] kw = this.kw; + final long[] t = this.t; + final int[] mod5 = MOD5; + final int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.length != 9) + { + throw new IllegalArgumentException(); + } + if (t.length != 5) + { + throw new IllegalArgumentException(); + } + + /* + * Read 4 words of plaintext data, not using arrays for cipher state + */ + long b0 = block[0]; + long b1 = block[1]; + long b2 = block[2]; + long b3 = block[3]; + + /* + * First subkey injection. + */ + b0 += kw[0]; + b1 += kw[1] + t[0]; + b2 += kw[2] + t[1]; + b3 += kw[3]; + + /* + * Rounds loop, unrolled to 8 rounds per iteration. + * + * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows + * inlining of the permutations, which cycle every of 2 rounds (avoiding array + * index/lookup). + * + * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows + * inlining constant rotation values (avoiding array index/lookup). + */ + + for (int d = 1; d < (ROUNDS_256 / 4); d += 2) + { + final int dm5 = mod5[d]; + final int dm3 = mod3[d]; + + /* + * 4 rounds of mix and permute. + * + * Permute schedule has a 2 round cycle, so permutes are inlined in the mix + * operations in each 4 round block. + */ + b1 = rotlXor(b1, ROTATION_0_0, b0 += b1); + b3 = rotlXor(b3, ROTATION_0_1, b2 += b3); + + b3 = rotlXor(b3, ROTATION_1_0, b0 += b3); + b1 = rotlXor(b1, ROTATION_1_1, b2 += b1); + + b1 = rotlXor(b1, ROTATION_2_0, b0 += b1); + b3 = rotlXor(b3, ROTATION_2_1, b2 += b3); + + b3 = rotlXor(b3, ROTATION_3_0, b0 += b3); + b1 = rotlXor(b1, ROTATION_3_1, b2 += b1); + + /* + * Subkey injection for first 4 rounds. + */ + b0 += kw[dm5]; + b1 += kw[dm5 + 1] + t[dm3]; + b2 += kw[dm5 + 2] + t[dm3 + 1]; + b3 += kw[dm5 + 3] + d; + + /* + * 4 more rounds of mix/permute + */ + b1 = rotlXor(b1, ROTATION_4_0, b0 += b1); + b3 = rotlXor(b3, ROTATION_4_1, b2 += b3); + + b3 = rotlXor(b3, ROTATION_5_0, b0 += b3); + b1 = rotlXor(b1, ROTATION_5_1, b2 += b1); + + b1 = rotlXor(b1, ROTATION_6_0, b0 += b1); + b3 = rotlXor(b3, ROTATION_6_1, b2 += b3); + + b3 = rotlXor(b3, ROTATION_7_0, b0 += b3); + b1 = rotlXor(b1, ROTATION_7_1, b2 += b1); + + /* + * Subkey injection for next 4 rounds. + */ + b0 += kw[dm5 + 1]; + b1 += kw[dm5 + 2] + t[dm3 + 1]; + b2 += kw[dm5 + 3] + t[dm3 + 2]; + b3 += kw[dm5 + 4] + d + 1; + } + + /* + * Output cipher state. + */ + out[0] = b0; + out[1] = b1; + out[2] = b2; + out[3] = b3; + } + + void decryptBlock(long[] block, long[] state) + { + final long[] kw = this.kw; + final long[] t = this.t; + final int[] mod5 = MOD5; + final int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.length != 9) + { + throw new IllegalArgumentException(); + } + if (t.length != 5) + { + throw new IllegalArgumentException(); + } + + long b0 = block[0]; + long b1 = block[1]; + long b2 = block[2]; + long b3 = block[3]; + + for (int d = (ROUNDS_256 / 4) - 1; d >= 1; d -= 2) + { + final int dm5 = mod5[d]; + final int dm3 = mod3[d]; + + /* Reverse key injection for second 4 rounds */ + b0 -= kw[dm5 + 1]; + b1 -= kw[dm5 + 2] + t[dm3 + 1]; + b2 -= kw[dm5 + 3] + t[dm3 + 2]; + b3 -= kw[dm5 + 4] + d + 1; + + /* Reverse second 4 mix/permute rounds */ + + b3 = xorRotr(b3, ROTATION_7_0, b0); + b0 -= b3; + b1 = xorRotr(b1, ROTATION_7_1, b2); + b2 -= b1; + + b1 = xorRotr(b1, ROTATION_6_0, b0); + b0 -= b1; + b3 = xorRotr(b3, ROTATION_6_1, b2); + b2 -= b3; + + b3 = xorRotr(b3, ROTATION_5_0, b0); + b0 -= b3; + b1 = xorRotr(b1, ROTATION_5_1, b2); + b2 -= b1; + + b1 = xorRotr(b1, ROTATION_4_0, b0); + b0 -= b1; + b3 = xorRotr(b3, ROTATION_4_1, b2); + b2 -= b3; + + /* Reverse key injection for first 4 rounds */ + b0 -= kw[dm5]; + b1 -= kw[dm5 + 1] + t[dm3]; + b2 -= kw[dm5 + 2] + t[dm3 + 1]; + b3 -= kw[dm5 + 3] + d; + + /* Reverse first 4 mix/permute rounds */ + b3 = xorRotr(b3, ROTATION_3_0, b0); + b0 -= b3; + b1 = xorRotr(b1, ROTATION_3_1, b2); + b2 -= b1; + + b1 = xorRotr(b1, ROTATION_2_0, b0); + b0 -= b1; + b3 = xorRotr(b3, ROTATION_2_1, b2); + b2 -= b3; + + b3 = xorRotr(b3, ROTATION_1_0, b0); + b0 -= b3; + b1 = xorRotr(b1, ROTATION_1_1, b2); + b2 -= b1; + + b1 = xorRotr(b1, ROTATION_0_0, b0); + b0 -= b1; + b3 = xorRotr(b3, ROTATION_0_1, b2); + b2 -= b3; + } + + /* + * First subkey uninjection. + */ + b0 -= kw[0]; + b1 -= kw[1] + t[0]; + b2 -= kw[2] + t[1]; + b3 -= kw[3]; + + /* + * Output cipher state. + */ + state[0] = b0; + state[1] = b1; + state[2] = b2; + state[3] = b3; + } + + } + + private static final class Threefish512Cipher + extends ThreefishCipher + { + /** + * Mix rotation constants defined in Skein 1.3 specification + */ + private static final int ROTATION_0_0 = 46, ROTATION_0_1 = 36, ROTATION_0_2 = 19, ROTATION_0_3 = 37; + private static final int ROTATION_1_0 = 33, ROTATION_1_1 = 27, ROTATION_1_2 = 14, ROTATION_1_3 = 42; + private static final int ROTATION_2_0 = 17, ROTATION_2_1 = 49, ROTATION_2_2 = 36, ROTATION_2_3 = 39; + private static final int ROTATION_3_0 = 44, ROTATION_3_1 = 9, ROTATION_3_2 = 54, ROTATION_3_3 = 56; + + private static final int ROTATION_4_0 = 39, ROTATION_4_1 = 30, ROTATION_4_2 = 34, ROTATION_4_3 = 24; + private static final int ROTATION_5_0 = 13, ROTATION_5_1 = 50, ROTATION_5_2 = 10, ROTATION_5_3 = 17; + private static final int ROTATION_6_0 = 25, ROTATION_6_1 = 29, ROTATION_6_2 = 39, ROTATION_6_3 = 43; + private static final int ROTATION_7_0 = 8, ROTATION_7_1 = 35, ROTATION_7_2 = 56, ROTATION_7_3 = 22; + + protected Threefish512Cipher(long[] kw, long[] t) + { + super(kw, t); + } + + public void encryptBlock(long[] block, long[] out) + { + final long[] kw = this.kw; + final long[] t = this.t; + final int[] mod9 = MOD9; + final int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.length != 17) + { + throw new IllegalArgumentException(); + } + if (t.length != 5) + { + throw new IllegalArgumentException(); + } + + /* + * Read 8 words of plaintext data, not using arrays for cipher state + */ + long b0 = block[0]; + long b1 = block[1]; + long b2 = block[2]; + long b3 = block[3]; + long b4 = block[4]; + long b5 = block[5]; + long b6 = block[6]; + long b7 = block[7]; + + /* + * First subkey injection. + */ + b0 += kw[0]; + b1 += kw[1]; + b2 += kw[2]; + b3 += kw[3]; + b4 += kw[4]; + b5 += kw[5] + t[0]; + b6 += kw[6] + t[1]; + b7 += kw[7]; + + /* + * Rounds loop, unrolled to 8 rounds per iteration. + * + * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows + * inlining of the permutations, which cycle every of 4 rounds (avoiding array + * index/lookup). + * + * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows + * inlining constant rotation values (avoiding array index/lookup). + */ + + for (int d = 1; d < (ROUNDS_512 / 4); d += 2) + { + final int dm9 = mod9[d]; + final int dm3 = mod3[d]; + + /* + * 4 rounds of mix and permute. + * + * Permute schedule has a 4 round cycle, so permutes are inlined in the mix + * operations in each 4 round block. + */ + b1 = rotlXor(b1, ROTATION_0_0, b0 += b1); + b3 = rotlXor(b3, ROTATION_0_1, b2 += b3); + b5 = rotlXor(b5, ROTATION_0_2, b4 += b5); + b7 = rotlXor(b7, ROTATION_0_3, b6 += b7); + + b1 = rotlXor(b1, ROTATION_1_0, b2 += b1); + b7 = rotlXor(b7, ROTATION_1_1, b4 += b7); + b5 = rotlXor(b5, ROTATION_1_2, b6 += b5); + b3 = rotlXor(b3, ROTATION_1_3, b0 += b3); + + b1 = rotlXor(b1, ROTATION_2_0, b4 += b1); + b3 = rotlXor(b3, ROTATION_2_1, b6 += b3); + b5 = rotlXor(b5, ROTATION_2_2, b0 += b5); + b7 = rotlXor(b7, ROTATION_2_3, b2 += b7); + + b1 = rotlXor(b1, ROTATION_3_0, b6 += b1); + b7 = rotlXor(b7, ROTATION_3_1, b0 += b7); + b5 = rotlXor(b5, ROTATION_3_2, b2 += b5); + b3 = rotlXor(b3, ROTATION_3_3, b4 += b3); + + /* + * Subkey injection for first 4 rounds. + */ + b0 += kw[dm9]; + b1 += kw[dm9 + 1]; + b2 += kw[dm9 + 2]; + b3 += kw[dm9 + 3]; + b4 += kw[dm9 + 4]; + b5 += kw[dm9 + 5] + t[dm3]; + b6 += kw[dm9 + 6] + t[dm3 + 1]; + b7 += kw[dm9 + 7] + d; + + /* + * 4 more rounds of mix/permute + */ + b1 = rotlXor(b1, ROTATION_4_0, b0 += b1); + b3 = rotlXor(b3, ROTATION_4_1, b2 += b3); + b5 = rotlXor(b5, ROTATION_4_2, b4 += b5); + b7 = rotlXor(b7, ROTATION_4_3, b6 += b7); + + b1 = rotlXor(b1, ROTATION_5_0, b2 += b1); + b7 = rotlXor(b7, ROTATION_5_1, b4 += b7); + b5 = rotlXor(b5, ROTATION_5_2, b6 += b5); + b3 = rotlXor(b3, ROTATION_5_3, b0 += b3); + + b1 = rotlXor(b1, ROTATION_6_0, b4 += b1); + b3 = rotlXor(b3, ROTATION_6_1, b6 += b3); + b5 = rotlXor(b5, ROTATION_6_2, b0 += b5); + b7 = rotlXor(b7, ROTATION_6_3, b2 += b7); + + b1 = rotlXor(b1, ROTATION_7_0, b6 += b1); + b7 = rotlXor(b7, ROTATION_7_1, b0 += b7); + b5 = rotlXor(b5, ROTATION_7_2, b2 += b5); + b3 = rotlXor(b3, ROTATION_7_3, b4 += b3); + + /* + * Subkey injection for next 4 rounds. + */ + b0 += kw[dm9 + 1]; + b1 += kw[dm9 + 2]; + b2 += kw[dm9 + 3]; + b3 += kw[dm9 + 4]; + b4 += kw[dm9 + 5]; + b5 += kw[dm9 + 6] + t[dm3 + 1]; + b6 += kw[dm9 + 7] + t[dm3 + 2]; + b7 += kw[dm9 + 8] + d + 1; + } + + /* + * Output cipher state. + */ + out[0] = b0; + out[1] = b1; + out[2] = b2; + out[3] = b3; + out[4] = b4; + out[5] = b5; + out[6] = b6; + out[7] = b7; + } + + public void decryptBlock(long[] block, long[] state) + { + final long[] kw = this.kw; + final long[] t = this.t; + final int[] mod9 = MOD9; + final int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.length != 17) + { + throw new IllegalArgumentException(); + } + if (t.length != 5) + { + throw new IllegalArgumentException(); + } + + long b0 = block[0]; + long b1 = block[1]; + long b2 = block[2]; + long b3 = block[3]; + long b4 = block[4]; + long b5 = block[5]; + long b6 = block[6]; + long b7 = block[7]; + + for (int d = (ROUNDS_512 / 4) - 1; d >= 1; d -= 2) + { + final int dm9 = mod9[d]; + final int dm3 = mod3[d]; + + /* Reverse key injection for second 4 rounds */ + b0 -= kw[dm9 + 1]; + b1 -= kw[dm9 + 2]; + b2 -= kw[dm9 + 3]; + b3 -= kw[dm9 + 4]; + b4 -= kw[dm9 + 5]; + b5 -= kw[dm9 + 6] + t[dm3 + 1]; + b6 -= kw[dm9 + 7] + t[dm3 + 2]; + b7 -= kw[dm9 + 8] + d + 1; + + /* Reverse second 4 mix/permute rounds */ + + b1 = xorRotr(b1, ROTATION_7_0, b6); + b6 -= b1; + b7 = xorRotr(b7, ROTATION_7_1, b0); + b0 -= b7; + b5 = xorRotr(b5, ROTATION_7_2, b2); + b2 -= b5; + b3 = xorRotr(b3, ROTATION_7_3, b4); + b4 -= b3; + + b1 = xorRotr(b1, ROTATION_6_0, b4); + b4 -= b1; + b3 = xorRotr(b3, ROTATION_6_1, b6); + b6 -= b3; + b5 = xorRotr(b5, ROTATION_6_2, b0); + b0 -= b5; + b7 = xorRotr(b7, ROTATION_6_3, b2); + b2 -= b7; + + b1 = xorRotr(b1, ROTATION_5_0, b2); + b2 -= b1; + b7 = xorRotr(b7, ROTATION_5_1, b4); + b4 -= b7; + b5 = xorRotr(b5, ROTATION_5_2, b6); + b6 -= b5; + b3 = xorRotr(b3, ROTATION_5_3, b0); + b0 -= b3; + + b1 = xorRotr(b1, ROTATION_4_0, b0); + b0 -= b1; + b3 = xorRotr(b3, ROTATION_4_1, b2); + b2 -= b3; + b5 = xorRotr(b5, ROTATION_4_2, b4); + b4 -= b5; + b7 = xorRotr(b7, ROTATION_4_3, b6); + b6 -= b7; + + /* Reverse key injection for first 4 rounds */ + b0 -= kw[dm9]; + b1 -= kw[dm9 + 1]; + b2 -= kw[dm9 + 2]; + b3 -= kw[dm9 + 3]; + b4 -= kw[dm9 + 4]; + b5 -= kw[dm9 + 5] + t[dm3]; + b6 -= kw[dm9 + 6] + t[dm3 + 1]; + b7 -= kw[dm9 + 7] + d; + + /* Reverse first 4 mix/permute rounds */ + b1 = xorRotr(b1, ROTATION_3_0, b6); + b6 -= b1; + b7 = xorRotr(b7, ROTATION_3_1, b0); + b0 -= b7; + b5 = xorRotr(b5, ROTATION_3_2, b2); + b2 -= b5; + b3 = xorRotr(b3, ROTATION_3_3, b4); + b4 -= b3; + + b1 = xorRotr(b1, ROTATION_2_0, b4); + b4 -= b1; + b3 = xorRotr(b3, ROTATION_2_1, b6); + b6 -= b3; + b5 = xorRotr(b5, ROTATION_2_2, b0); + b0 -= b5; + b7 = xorRotr(b7, ROTATION_2_3, b2); + b2 -= b7; + + b1 = xorRotr(b1, ROTATION_1_0, b2); + b2 -= b1; + b7 = xorRotr(b7, ROTATION_1_1, b4); + b4 -= b7; + b5 = xorRotr(b5, ROTATION_1_2, b6); + b6 -= b5; + b3 = xorRotr(b3, ROTATION_1_3, b0); + b0 -= b3; + + b1 = xorRotr(b1, ROTATION_0_0, b0); + b0 -= b1; + b3 = xorRotr(b3, ROTATION_0_1, b2); + b2 -= b3; + b5 = xorRotr(b5, ROTATION_0_2, b4); + b4 -= b5; + b7 = xorRotr(b7, ROTATION_0_3, b6); + b6 -= b7; + } + + /* + * First subkey uninjection. + */ + b0 -= kw[0]; + b1 -= kw[1]; + b2 -= kw[2]; + b3 -= kw[3]; + b4 -= kw[4]; + b5 -= kw[5] + t[0]; + b6 -= kw[6] + t[1]; + b7 -= kw[7]; + + /* + * Output cipher state. + */ + state[0] = b0; + state[1] = b1; + state[2] = b2; + state[3] = b3; + state[4] = b4; + state[5] = b5; + state[6] = b6; + state[7] = b7; + } + } + + private static final class Threefish1024Cipher + extends ThreefishCipher + { + /** + * Mix rotation constants defined in Skein 1.3 specification + */ + private static final int ROTATION_0_0 = 24, ROTATION_0_1 = 13, ROTATION_0_2 = 8, ROTATION_0_3 = 47; + private static final int ROTATION_0_4 = 8, ROTATION_0_5 = 17, ROTATION_0_6 = 22, ROTATION_0_7 = 37; + private static final int ROTATION_1_0 = 38, ROTATION_1_1 = 19, ROTATION_1_2 = 10, ROTATION_1_3 = 55; + private static final int ROTATION_1_4 = 49, ROTATION_1_5 = 18, ROTATION_1_6 = 23, ROTATION_1_7 = 52; + private static final int ROTATION_2_0 = 33, ROTATION_2_1 = 4, ROTATION_2_2 = 51, ROTATION_2_3 = 13; + private static final int ROTATION_2_4 = 34, ROTATION_2_5 = 41, ROTATION_2_6 = 59, ROTATION_2_7 = 17; + private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 20, ROTATION_3_2 = 48, ROTATION_3_3 = 41; + private static final int ROTATION_3_4 = 47, ROTATION_3_5 = 28, ROTATION_3_6 = 16, ROTATION_3_7 = 25; + + private static final int ROTATION_4_0 = 41, ROTATION_4_1 = 9, ROTATION_4_2 = 37, ROTATION_4_3 = 31; + private static final int ROTATION_4_4 = 12, ROTATION_4_5 = 47, ROTATION_4_6 = 44, ROTATION_4_7 = 30; + private static final int ROTATION_5_0 = 16, ROTATION_5_1 = 34, ROTATION_5_2 = 56, ROTATION_5_3 = 51; + private static final int ROTATION_5_4 = 4, ROTATION_5_5 = 53, ROTATION_5_6 = 42, ROTATION_5_7 = 41; + private static final int ROTATION_6_0 = 31, ROTATION_6_1 = 44, ROTATION_6_2 = 47, ROTATION_6_3 = 46; + private static final int ROTATION_6_4 = 19, ROTATION_6_5 = 42, ROTATION_6_6 = 44, ROTATION_6_7 = 25; + private static final int ROTATION_7_0 = 9, ROTATION_7_1 = 48, ROTATION_7_2 = 35, ROTATION_7_3 = 52; + private static final int ROTATION_7_4 = 23, ROTATION_7_5 = 31, ROTATION_7_6 = 37, ROTATION_7_7 = 20; + + public Threefish1024Cipher(long[] kw, long[] t) + { + super(kw, t); + } + + void encryptBlock(long[] block, long[] out) + { + final long[] kw = this.kw; + final long[] t = this.t; + final int[] mod17 = MOD17; + final int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.length != 33) + { + throw new IllegalArgumentException(); + } + if (t.length != 5) + { + throw new IllegalArgumentException(); + } + + /* + * Read 16 words of plaintext data, not using arrays for cipher state + */ + long b0 = block[0]; + long b1 = block[1]; + long b2 = block[2]; + long b3 = block[3]; + long b4 = block[4]; + long b5 = block[5]; + long b6 = block[6]; + long b7 = block[7]; + long b8 = block[8]; + long b9 = block[9]; + long b10 = block[10]; + long b11 = block[11]; + long b12 = block[12]; + long b13 = block[13]; + long b14 = block[14]; + long b15 = block[15]; + + /* + * First subkey injection. + */ + b0 += kw[0]; + b1 += kw[1]; + b2 += kw[2]; + b3 += kw[3]; + b4 += kw[4]; + b5 += kw[5]; + b6 += kw[6]; + b7 += kw[7]; + b8 += kw[8]; + b9 += kw[9]; + b10 += kw[10]; + b11 += kw[11]; + b12 += kw[12]; + b13 += kw[13] + t[0]; + b14 += kw[14] + t[1]; + b15 += kw[15]; + + /* + * Rounds loop, unrolled to 8 rounds per iteration. + * + * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows + * inlining of the permutations, which cycle every of 4 rounds (avoiding array + * index/lookup). + * + * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows + * inlining constant rotation values (avoiding array index/lookup). + */ + + for (int d = 1; d < (ROUNDS_1024 / 4); d += 2) + { + final int dm17 = mod17[d]; + final int dm3 = mod3[d]; + + /* + * 4 rounds of mix and permute. + * + * Permute schedule has a 4 round cycle, so permutes are inlined in the mix + * operations in each 4 round block. + */ + b1 = rotlXor(b1, ROTATION_0_0, b0 += b1); + b3 = rotlXor(b3, ROTATION_0_1, b2 += b3); + b5 = rotlXor(b5, ROTATION_0_2, b4 += b5); + b7 = rotlXor(b7, ROTATION_0_3, b6 += b7); + b9 = rotlXor(b9, ROTATION_0_4, b8 += b9); + b11 = rotlXor(b11, ROTATION_0_5, b10 += b11); + b13 = rotlXor(b13, ROTATION_0_6, b12 += b13); + b15 = rotlXor(b15, ROTATION_0_7, b14 += b15); + + b9 = rotlXor(b9, ROTATION_1_0, b0 += b9); + b13 = rotlXor(b13, ROTATION_1_1, b2 += b13); + b11 = rotlXor(b11, ROTATION_1_2, b6 += b11); + b15 = rotlXor(b15, ROTATION_1_3, b4 += b15); + b7 = rotlXor(b7, ROTATION_1_4, b10 += b7); + b3 = rotlXor(b3, ROTATION_1_5, b12 += b3); + b5 = rotlXor(b5, ROTATION_1_6, b14 += b5); + b1 = rotlXor(b1, ROTATION_1_7, b8 += b1); + + b7 = rotlXor(b7, ROTATION_2_0, b0 += b7); + b5 = rotlXor(b5, ROTATION_2_1, b2 += b5); + b3 = rotlXor(b3, ROTATION_2_2, b4 += b3); + b1 = rotlXor(b1, ROTATION_2_3, b6 += b1); + b15 = rotlXor(b15, ROTATION_2_4, b12 += b15); + b13 = rotlXor(b13, ROTATION_2_5, b14 += b13); + b11 = rotlXor(b11, ROTATION_2_6, b8 += b11); + b9 = rotlXor(b9, ROTATION_2_7, b10 += b9); + + b15 = rotlXor(b15, ROTATION_3_0, b0 += b15); + b11 = rotlXor(b11, ROTATION_3_1, b2 += b11); + b13 = rotlXor(b13, ROTATION_3_2, b6 += b13); + b9 = rotlXor(b9, ROTATION_3_3, b4 += b9); + b1 = rotlXor(b1, ROTATION_3_4, b14 += b1); + b5 = rotlXor(b5, ROTATION_3_5, b8 += b5); + b3 = rotlXor(b3, ROTATION_3_6, b10 += b3); + b7 = rotlXor(b7, ROTATION_3_7, b12 += b7); + + /* + * Subkey injection for first 4 rounds. + */ + b0 += kw[dm17]; + b1 += kw[dm17 + 1]; + b2 += kw[dm17 + 2]; + b3 += kw[dm17 + 3]; + b4 += kw[dm17 + 4]; + b5 += kw[dm17 + 5]; + b6 += kw[dm17 + 6]; + b7 += kw[dm17 + 7]; + b8 += kw[dm17 + 8]; + b9 += kw[dm17 + 9]; + b10 += kw[dm17 + 10]; + b11 += kw[dm17 + 11]; + b12 += kw[dm17 + 12]; + b13 += kw[dm17 + 13] + t[dm3]; + b14 += kw[dm17 + 14] + t[dm3 + 1]; + b15 += kw[dm17 + 15] + d; + + /* + * 4 more rounds of mix/permute + */ + b1 = rotlXor(b1, ROTATION_4_0, b0 += b1); + b3 = rotlXor(b3, ROTATION_4_1, b2 += b3); + b5 = rotlXor(b5, ROTATION_4_2, b4 += b5); + b7 = rotlXor(b7, ROTATION_4_3, b6 += b7); + b9 = rotlXor(b9, ROTATION_4_4, b8 += b9); + b11 = rotlXor(b11, ROTATION_4_5, b10 += b11); + b13 = rotlXor(b13, ROTATION_4_6, b12 += b13); + b15 = rotlXor(b15, ROTATION_4_7, b14 += b15); + + b9 = rotlXor(b9, ROTATION_5_0, b0 += b9); + b13 = rotlXor(b13, ROTATION_5_1, b2 += b13); + b11 = rotlXor(b11, ROTATION_5_2, b6 += b11); + b15 = rotlXor(b15, ROTATION_5_3, b4 += b15); + b7 = rotlXor(b7, ROTATION_5_4, b10 += b7); + b3 = rotlXor(b3, ROTATION_5_5, b12 += b3); + b5 = rotlXor(b5, ROTATION_5_6, b14 += b5); + b1 = rotlXor(b1, ROTATION_5_7, b8 += b1); + + b7 = rotlXor(b7, ROTATION_6_0, b0 += b7); + b5 = rotlXor(b5, ROTATION_6_1, b2 += b5); + b3 = rotlXor(b3, ROTATION_6_2, b4 += b3); + b1 = rotlXor(b1, ROTATION_6_3, b6 += b1); + b15 = rotlXor(b15, ROTATION_6_4, b12 += b15); + b13 = rotlXor(b13, ROTATION_6_5, b14 += b13); + b11 = rotlXor(b11, ROTATION_6_6, b8 += b11); + b9 = rotlXor(b9, ROTATION_6_7, b10 += b9); + + b15 = rotlXor(b15, ROTATION_7_0, b0 += b15); + b11 = rotlXor(b11, ROTATION_7_1, b2 += b11); + b13 = rotlXor(b13, ROTATION_7_2, b6 += b13); + b9 = rotlXor(b9, ROTATION_7_3, b4 += b9); + b1 = rotlXor(b1, ROTATION_7_4, b14 += b1); + b5 = rotlXor(b5, ROTATION_7_5, b8 += b5); + b3 = rotlXor(b3, ROTATION_7_6, b10 += b3); + b7 = rotlXor(b7, ROTATION_7_7, b12 += b7); + + /* + * Subkey injection for next 4 rounds. + */ + b0 += kw[dm17 + 1]; + b1 += kw[dm17 + 2]; + b2 += kw[dm17 + 3]; + b3 += kw[dm17 + 4]; + b4 += kw[dm17 + 5]; + b5 += kw[dm17 + 6]; + b6 += kw[dm17 + 7]; + b7 += kw[dm17 + 8]; + b8 += kw[dm17 + 9]; + b9 += kw[dm17 + 10]; + b10 += kw[dm17 + 11]; + b11 += kw[dm17 + 12]; + b12 += kw[dm17 + 13]; + b13 += kw[dm17 + 14] + t[dm3 + 1]; + b14 += kw[dm17 + 15] + t[dm3 + 2]; + b15 += kw[dm17 + 16] + d + 1; + + } + + /* + * Output cipher state. + */ + out[0] = b0; + out[1] = b1; + out[2] = b2; + out[3] = b3; + out[4] = b4; + out[5] = b5; + out[6] = b6; + out[7] = b7; + out[8] = b8; + out[9] = b9; + out[10] = b10; + out[11] = b11; + out[12] = b12; + out[13] = b13; + out[14] = b14; + out[15] = b15; + } + + void decryptBlock(long[] block, long[] state) + { + final long[] kw = this.kw; + final long[] t = this.t; + final int[] mod17 = MOD17; + final int[] mod3 = MOD3; + + /* Help the JIT avoid index bounds checks */ + if (kw.length != 33) + { + throw new IllegalArgumentException(); + } + if (t.length != 5) + { + throw new IllegalArgumentException(); + } + + long b0 = block[0]; + long b1 = block[1]; + long b2 = block[2]; + long b3 = block[3]; + long b4 = block[4]; + long b5 = block[5]; + long b6 = block[6]; + long b7 = block[7]; + long b8 = block[8]; + long b9 = block[9]; + long b10 = block[10]; + long b11 = block[11]; + long b12 = block[12]; + long b13 = block[13]; + long b14 = block[14]; + long b15 = block[15]; + + for (int d = (ROUNDS_1024 / 4) - 1; d >= 1; d -= 2) + { + final int dm17 = mod17[d]; + final int dm3 = mod3[d]; + + /* Reverse key injection for second 4 rounds */ + b0 -= kw[dm17 + 1]; + b1 -= kw[dm17 + 2]; + b2 -= kw[dm17 + 3]; + b3 -= kw[dm17 + 4]; + b4 -= kw[dm17 + 5]; + b5 -= kw[dm17 + 6]; + b6 -= kw[dm17 + 7]; + b7 -= kw[dm17 + 8]; + b8 -= kw[dm17 + 9]; + b9 -= kw[dm17 + 10]; + b10 -= kw[dm17 + 11]; + b11 -= kw[dm17 + 12]; + b12 -= kw[dm17 + 13]; + b13 -= kw[dm17 + 14] + t[dm3 + 1]; + b14 -= kw[dm17 + 15] + t[dm3 + 2]; + b15 -= kw[dm17 + 16] + d + 1; + + /* Reverse second 4 mix/permute rounds */ + b15 = xorRotr(b15, ROTATION_7_0, b0); + b0 -= b15; + b11 = xorRotr(b11, ROTATION_7_1, b2); + b2 -= b11; + b13 = xorRotr(b13, ROTATION_7_2, b6); + b6 -= b13; + b9 = xorRotr(b9, ROTATION_7_3, b4); + b4 -= b9; + b1 = xorRotr(b1, ROTATION_7_4, b14); + b14 -= b1; + b5 = xorRotr(b5, ROTATION_7_5, b8); + b8 -= b5; + b3 = xorRotr(b3, ROTATION_7_6, b10); + b10 -= b3; + b7 = xorRotr(b7, ROTATION_7_7, b12); + b12 -= b7; + + b7 = xorRotr(b7, ROTATION_6_0, b0); + b0 -= b7; + b5 = xorRotr(b5, ROTATION_6_1, b2); + b2 -= b5; + b3 = xorRotr(b3, ROTATION_6_2, b4); + b4 -= b3; + b1 = xorRotr(b1, ROTATION_6_3, b6); + b6 -= b1; + b15 = xorRotr(b15, ROTATION_6_4, b12); + b12 -= b15; + b13 = xorRotr(b13, ROTATION_6_5, b14); + b14 -= b13; + b11 = xorRotr(b11, ROTATION_6_6, b8); + b8 -= b11; + b9 = xorRotr(b9, ROTATION_6_7, b10); + b10 -= b9; + + b9 = xorRotr(b9, ROTATION_5_0, b0); + b0 -= b9; + b13 = xorRotr(b13, ROTATION_5_1, b2); + b2 -= b13; + b11 = xorRotr(b11, ROTATION_5_2, b6); + b6 -= b11; + b15 = xorRotr(b15, ROTATION_5_3, b4); + b4 -= b15; + b7 = xorRotr(b7, ROTATION_5_4, b10); + b10 -= b7; + b3 = xorRotr(b3, ROTATION_5_5, b12); + b12 -= b3; + b5 = xorRotr(b5, ROTATION_5_6, b14); + b14 -= b5; + b1 = xorRotr(b1, ROTATION_5_7, b8); + b8 -= b1; + + b1 = xorRotr(b1, ROTATION_4_0, b0); + b0 -= b1; + b3 = xorRotr(b3, ROTATION_4_1, b2); + b2 -= b3; + b5 = xorRotr(b5, ROTATION_4_2, b4); + b4 -= b5; + b7 = xorRotr(b7, ROTATION_4_3, b6); + b6 -= b7; + b9 = xorRotr(b9, ROTATION_4_4, b8); + b8 -= b9; + b11 = xorRotr(b11, ROTATION_4_5, b10); + b10 -= b11; + b13 = xorRotr(b13, ROTATION_4_6, b12); + b12 -= b13; + b15 = xorRotr(b15, ROTATION_4_7, b14); + b14 -= b15; + + /* Reverse key injection for first 4 rounds */ + b0 -= kw[dm17]; + b1 -= kw[dm17 + 1]; + b2 -= kw[dm17 + 2]; + b3 -= kw[dm17 + 3]; + b4 -= kw[dm17 + 4]; + b5 -= kw[dm17 + 5]; + b6 -= kw[dm17 + 6]; + b7 -= kw[dm17 + 7]; + b8 -= kw[dm17 + 8]; + b9 -= kw[dm17 + 9]; + b10 -= kw[dm17 + 10]; + b11 -= kw[dm17 + 11]; + b12 -= kw[dm17 + 12]; + b13 -= kw[dm17 + 13] + t[dm3]; + b14 -= kw[dm17 + 14] + t[dm3 + 1]; + b15 -= kw[dm17 + 15] + d; + + /* Reverse first 4 mix/permute rounds */ + b15 = xorRotr(b15, ROTATION_3_0, b0); + b0 -= b15; + b11 = xorRotr(b11, ROTATION_3_1, b2); + b2 -= b11; + b13 = xorRotr(b13, ROTATION_3_2, b6); + b6 -= b13; + b9 = xorRotr(b9, ROTATION_3_3, b4); + b4 -= b9; + b1 = xorRotr(b1, ROTATION_3_4, b14); + b14 -= b1; + b5 = xorRotr(b5, ROTATION_3_5, b8); + b8 -= b5; + b3 = xorRotr(b3, ROTATION_3_6, b10); + b10 -= b3; + b7 = xorRotr(b7, ROTATION_3_7, b12); + b12 -= b7; + + b7 = xorRotr(b7, ROTATION_2_0, b0); + b0 -= b7; + b5 = xorRotr(b5, ROTATION_2_1, b2); + b2 -= b5; + b3 = xorRotr(b3, ROTATION_2_2, b4); + b4 -= b3; + b1 = xorRotr(b1, ROTATION_2_3, b6); + b6 -= b1; + b15 = xorRotr(b15, ROTATION_2_4, b12); + b12 -= b15; + b13 = xorRotr(b13, ROTATION_2_5, b14); + b14 -= b13; + b11 = xorRotr(b11, ROTATION_2_6, b8); + b8 -= b11; + b9 = xorRotr(b9, ROTATION_2_7, b10); + b10 -= b9; + + b9 = xorRotr(b9, ROTATION_1_0, b0); + b0 -= b9; + b13 = xorRotr(b13, ROTATION_1_1, b2); + b2 -= b13; + b11 = xorRotr(b11, ROTATION_1_2, b6); + b6 -= b11; + b15 = xorRotr(b15, ROTATION_1_3, b4); + b4 -= b15; + b7 = xorRotr(b7, ROTATION_1_4, b10); + b10 -= b7; + b3 = xorRotr(b3, ROTATION_1_5, b12); + b12 -= b3; + b5 = xorRotr(b5, ROTATION_1_6, b14); + b14 -= b5; + b1 = xorRotr(b1, ROTATION_1_7, b8); + b8 -= b1; + + b1 = xorRotr(b1, ROTATION_0_0, b0); + b0 -= b1; + b3 = xorRotr(b3, ROTATION_0_1, b2); + b2 -= b3; + b5 = xorRotr(b5, ROTATION_0_2, b4); + b4 -= b5; + b7 = xorRotr(b7, ROTATION_0_3, b6); + b6 -= b7; + b9 = xorRotr(b9, ROTATION_0_4, b8); + b8 -= b9; + b11 = xorRotr(b11, ROTATION_0_5, b10); + b10 -= b11; + b13 = xorRotr(b13, ROTATION_0_6, b12); + b12 -= b13; + b15 = xorRotr(b15, ROTATION_0_7, b14); + b14 -= b15; + } + + /* + * First subkey uninjection. + */ + b0 -= kw[0]; + b1 -= kw[1]; + b2 -= kw[2]; + b3 -= kw[3]; + b4 -= kw[4]; + b5 -= kw[5]; + b6 -= kw[6]; + b7 -= kw[7]; + b8 -= kw[8]; + b9 -= kw[9]; + b10 -= kw[10]; + b11 -= kw[11]; + b12 -= kw[12]; + b13 -= kw[13] + t[0]; + b14 -= kw[14] + t[1]; + b15 -= kw[15]; + + /* + * Output cipher state. + */ + state[0] = b0; + state[1] = b1; + state[2] = b2; + state[3] = b3; + state[4] = b4; + state[5] = b5; + state[6] = b6; + state[7] = b7; + state[8] = b8; + state[9] = b9; + state[10] = b10; + state[11] = b11; + state[12] = b12; + state[13] = b13; + state[14] = b14; + state[15] = b15; + } + + } + +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java index 0703fd6..f16f6d4 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java @@ -44,7 +44,6 @@ public class VMPCEngine implements StreamCipher } ParametersWithIV ivParams = (ParametersWithIV) params; - KeyParameter key = (KeyParameter) ivParams.getParameters(); if (!(ivParams.getParameters() instanceof KeyParameter)) { @@ -52,6 +51,8 @@ public class VMPCEngine implements StreamCipher "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) diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java new file mode 100644 index 0000000..5b2181d --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java @@ -0,0 +1,65 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.util.Pack; + +/** + * Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce. + * <p> + * 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/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java index f037da4..fb21bbc 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java @@ -107,6 +107,11 @@ public class XTEAEngine 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) { diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html deleted file mode 100644 index e945dac..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Basic cipher classes. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html deleted file mode 100644 index 390a540..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Simple examples of light weight API usage. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java index 2ef8dd2..16c8b91 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java @@ -1,9 +1,9 @@ package org.bouncycastle.crypto.generators; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.DerivationFunction; import org.bouncycastle.crypto.DerivationParameters; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.DigestDerivationFunction; import org.bouncycastle.crypto.params.ISO18033KDFParameters; import org.bouncycastle.crypto.params.KDFParameters; import org.bouncycastle.crypto.util.Pack; @@ -13,7 +13,8 @@ import org.bouncycastle.crypto.util.Pack; * 18033 <br> * This implementation is based on ISO 18033/P1363a. */ -public class BaseKDFBytesGenerator implements DerivationFunction +public class BaseKDFBytesGenerator + implements DigestDerivationFunction { private int counterStart; private Digest digest; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java index 749b0cc..f7a3df2 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java @@ -226,10 +226,10 @@ public class DSAParametersGenerator int seedlen = N; byte[] seed = new byte[seedlen / 8]; -// 3. n = ceiling(L ⁄ outlen) – 1. +// 3. n = ceiling(L / outlen) - 1. int n = (L - 1) / outlen; -// 4. b = L – 1 – (n ∗ outlen). +// 4. b = L - 1 - (n * outlen). int b = (L - 1) % outlen; byte[] output = new byte[d.getDigestSize()]; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java index d77bd74..d5f5fc8 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java @@ -26,6 +26,11 @@ public class ECKeyPairGenerator this.random = ecP.getRandom(); this.params = ecP.getDomainParameters(); + + if (this.random == null) + { + this.random = new SecureRandom(); + } } /** diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java new file mode 100644 index 0000000..306530e --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java @@ -0,0 +1,152 @@ +package org.bouncycastle.crypto.generators; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.MacDerivationFunction; +import org.bouncycastle.crypto.params.KDFCounterParameters; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java new file mode 100644 index 0000000..6115a1a --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java @@ -0,0 +1,181 @@ +package org.bouncycastle.crypto.generators; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.MacDerivationFunction; +import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java new file mode 100644 index 0000000..6003037 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java @@ -0,0 +1,175 @@ +package org.bouncycastle.crypto.generators; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.MacDerivationFunction; +import org.bouncycastle.crypto.params.KDFFeedbackParameters; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java index 640ead4..0954d48 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java @@ -58,7 +58,7 @@ public class PKCS5S2ParametersGenerator 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); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java new file mode 100644 index 0000000..9165973 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java @@ -0,0 +1,117 @@ +package org.bouncycastle.crypto.generators; + +import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.macs.Poly1305; + +/** + * Generates keys for the Poly1305 MAC. + * <p> + * 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. <br> + * The {@code r} value has a specific format with some bits required to be cleared, resulting in an + * effective 106 bit key. <br> + * 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.<br> + * 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. + * <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> 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 <code>r</code> (second 16 bytes) portion of the key.<br> + * Specifically: + * <ul> + * <li>r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})</li> + * <li>r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252})</li> + * </ul> + * + * @param a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code> + */ + 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. + * <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared + * as per {@link #clamp(byte[])}. + * + * @throws IllegalArgumentException if the key is of the wrong length, or has invalid bits set + * in the <code>r</code> 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/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html deleted file mode 100644 index 9d73ce3..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Generators for keys, key pairs and password based encryption algorithms. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java index bb09a76..93b04e9 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java @@ -5,15 +5,15 @@ import java.io.IOException; import java.io.InputStream; import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.StreamCipher; +import org.bouncycastle.crypto.modes.AEADBlockCipher; /** - * A CipherInputStream is composed of an InputStream and a BufferedBlockCipher 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. - * <p> + * 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. + * <p/> * 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. @@ -23,9 +23,10 @@ public class CipherInputStream { private BufferedBlockCipher bufferedBlockCipher; private StreamCipher streamCipher; + private AEADBlockCipher aeadBlockCipher; - private byte[] buf; - private byte[] inBuf; + private final byte[] buf; + private final byte[] inBuf; private int bufOff; private int maxBuf; @@ -62,95 +63,116 @@ public class CipherInputStream } /** - * grab the next chunk of input from the underlying input stream + * 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 { - int available = super.available(); - - // must always try to read 1 byte! - // some buggy InputStreams return < 0! - if (available <= 0) + if (finalized) { - available = 1; + return -1; } - if (available > inBuf.length) - { - available = super.read(inBuf, 0, inBuf.length); - } - else - { - available = super.read(inBuf, 0, available); - } + bufOff = 0; + maxBuf = 0; - if (available < 0) + // Keep reading until EOF or cipher processing produces data + while (maxBuf == 0) { - if (finalized) + int read = in.read(inBuf); + if (read == -1) { - return -1; + finaliseCipher(); + if (maxBuf == 0) + { + return -1; + } + return maxBuf; } try { if (bufferedBlockCipher != null) { - maxBuf = bufferedBlockCipher.doFinal(buf, 0); + maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, read, buf, 0); + } + else if (aeadBlockCipher != null) + { + maxBuf = aeadBlockCipher.processBytes(inBuf, 0, read, buf, 0); } else { - maxBuf = 0; // a stream cipher + streamCipher.processBytes(inBuf, 0, read, buf, 0); + maxBuf = read; } } catch (Exception e) { - throw new IOException("error processing stream: " + e.toString()); + throw new IOException("Error processing stream " + e); } + } + return maxBuf; + } - bufOff = 0; - + private void finaliseCipher() + throws IOException + { + try + { finalized = true; - - if (bufOff == maxBuf) + if (bufferedBlockCipher != null) { - return -1; + maxBuf = bufferedBlockCipher.doFinal(buf, 0); } - } - else - { - bufOff = 0; - - try + else if (aeadBlockCipher != null) { - if (bufferedBlockCipher != null) - { - maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, available, buf, 0); - } - else - { - streamCipher.processBytes(inBuf, 0, available, buf, 0); - maxBuf = available; - } + maxBuf = aeadBlockCipher.doFinal(buf, 0); } - catch (Exception e) + else { - throw new IOException("error processing stream: " + e.toString()); - } - - if (maxBuf == 0) // not enough bytes read for first block... - { - return nextChunk(); + maxBuf = 0; // a stream cipher } } - - return maxBuf; + 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. + * <p/> + * 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 (bufOff >= maxBuf) { if (nextChunk() < 0) { @@ -161,6 +183,19 @@ public class CipherInputStream 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 <code>b.length</code> bytes in the provided array. + * <p/> + * 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 <code>-1</code> 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 @@ -168,13 +203,28 @@ public class CipherInputStream 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 <code>len</code> bytes in the provided array. + * <p/> + * 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 <code>b</code> + * @param len the maximum number of bytes read. + * @return the total number of bytes read into the buffer, or <code>-1</code> 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 (bufOff >= maxBuf) { if (nextChunk() < 0) { @@ -182,22 +232,10 @@ public class CipherInputStream } } - int available = maxBuf - bufOff; - - if (len > available) - { - System.arraycopy(buf, bufOff, b, off, available); - bufOff = maxBuf; - - return available; - } - else - { - System.arraycopy(buf, bufOff, b, off, len); - bufOff += len; - - return len; - } + int toSupply = Math.min(len, available()); + System.arraycopy(buf, bufOff, b, off, toSupply); + bufOff += toSupply; + return toSupply; } public long skip( @@ -209,36 +247,55 @@ public class CipherInputStream return 0; } - int available = maxBuf - bufOff; + int skip = (int)Math.min(n, available()); + bufOff += skip; + return skip; + } - if (n > available) - { - bufOff = maxBuf; + public int available() + throws IOException + { + return maxBuf - bufOff; + } - return available; + /** + * 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(); } - else + finally { - bufOff += (int)n; - - return (int)n; + 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 int available() - throws IOException + public void mark(int readlimit) { - return maxBuf - bufOff; } - public void close() + public void reset() throws IOException { - super.close(); } public boolean markSupported() { return false; } + } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java index 17a7b6d..9beb5b9 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java @@ -5,15 +5,27 @@ import java.io.IOException; import java.io.OutputStream; import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.StreamCipher; +import org.bouncycastle.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. + * <p/> + * 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 byte[] oneByte = new byte[1]; + private final byte[] oneByte = new byte[1]; private byte[] buf; /** @@ -26,7 +38,6 @@ public class CipherOutputStream { super(os); this.bufferedBlockCipher = cipher; - this.buf = new byte[cipher.getBlockSize()]; } /** @@ -42,10 +53,19 @@ public class CipherOutputStream } /** + * 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 <code>byte</code>. - * @exception java.io.IOException if an I/O error occurs. + * @throws java.io.IOException if an I/O error occurs. */ public void write( int b) @@ -53,32 +73,27 @@ public class CipherOutputStream { oneByte[0] = (byte)b; - if (bufferedBlockCipher != null) + if (streamCipher != null) { - int len = bufferedBlockCipher.processBytes(oneByte, 0, 1, buf, 0); - - if (len != 0) - { - out.write(buf, 0, len); - } + out.write(streamCipher.returnByte((byte)b)); } else { - out.write(streamCipher.returnByte((byte)b)); + write(oneByte, 0, 1); } } /** * Writes <code>b.length</code> bytes from the specified byte array * to this output stream. - * <p> + * <p/> * The <code>write</code> method of * <code>CipherOutputStream</code> calls the <code>write</code> * method of three arguments with the three arguments * <code>b</code>, <code>0</code>, and <code>b.length</code>. * * @param b the data. - * @exception java.io.IOException if an I/O error occurs. + * @throws java.io.IOException if an I/O error occurs. * @see #write(byte[], int, int) */ public void write( @@ -92,10 +107,10 @@ public class CipherOutputStream * Writes <code>len</code> bytes from the specified byte array * starting at offset <code>off</code> to this output stream. * - * @param b the data. + * @param b the data. * @param off the start offset in the data. * @param len the number of bytes to write. - * @exception java.io.IOException if an I/O error occurs. + * @throws java.io.IOException if an I/O error occurs. */ public void write( byte[] b, @@ -103,10 +118,10 @@ public class CipherOutputStream int len) throws IOException { + ensureCapacity(len); + if (bufferedBlockCipher != null) { - byte[] buf = new byte[bufferedBlockCipher.getOutputSize(len)]; - int outLen = bufferedBlockCipher.processBytes(b, off, len, buf, 0); if (outLen != 0) @@ -114,10 +129,17 @@ public class CipherOutputStream out.write(buf, 0, outLen); } } - else + else if (aeadBlockCipher != null) { - byte[] buf = new byte[len]; + 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); @@ -125,49 +147,78 @@ public class CipherOutputStream } /** + * 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. - * - * <p> + * <p/> + * <p/> * 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 <code>write</code> methods is less than * the cipher's block size, no bytes will be written out. * - * @exception java.io.IOException if an I/O error occurs. + * @throws java.io.IOException if an I/O error occurs. */ public void flush() throws IOException { - super.flush(); + out.flush(); } /** * Closes this output stream and releases any system resources * associated with this stream. - * <p> + * <p/> * This method invokes the <code>doFinal</code> 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 * <code>flush</code> method of this output stream. - * <p> + * <p/> * This method resets the encapsulated cipher object to its initial state * and calls the <code>close</code> method of the underlying output * stream. * - * @exception java.io.IOException if an I/O error occurs. + * @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) { - byte[] buf = new byte[bufferedBlockCipher.getOutputSize(0)]; - int outLen = bufferedBlockCipher.doFinal(buf, 0); if (outLen != 0) @@ -175,14 +226,41 @@ public class CipherOutputStream 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) { - throw new IOException("Error closing stream: " + e.toString()); + error = new IOException("Error closing stream: " + e); } - flush(); - - super.close(); + 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/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java new file mode 100644 index 0000000..b601d4c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java @@ -0,0 +1,28 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html deleted file mode 100644 index f2c9e40..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Classes for doing "enhanced" I/O with Digests and MACs. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java b/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java index f4dfc6e..82ac20c 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java @@ -6,11 +6,13 @@ import java.security.SecureRandom; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DerivationFunction; import org.bouncycastle.crypto.KeyEncapsulation; +import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECKeyParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.params.KDFParameters; import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.BigIntegers; @@ -106,33 +108,34 @@ public class ECIESKeyEncapsulation throw new IllegalArgumentException("Public key required for encryption"); } - BigInteger n = key.getParameters().getN(); - BigInteger h = key.getParameters().getH(); + 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); - ECPoint gTilde = key.getParameters().getG().multiply(r); + + // 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); - // Compute the static-ephemeral key agreement - BigInteger rPrime; - if (CofactorMode) - { - rPrime = r.multiply(h).mod(n); - } - else - { - rPrime = r; - } - - ECPoint hTilde = ((ECPublicKeyParameters)key).getQ().multiply(rPrime); - // Encode the shared secret value - int PEHlen = (key.getParameters().getCurve().getFieldSize() + 7) / 8; - byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger()); + byte[] PEH = hTilde.getAffineXCoord().getEncoded(); // Initialise the KDF byte[] kdfInput; @@ -186,40 +189,36 @@ public class ECIESKeyEncapsulation throw new IllegalArgumentException("Private key required for encryption"); } - BigInteger n = key.getParameters().getN(); - BigInteger h = key.getParameters().getH(); + 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); - ECPoint gTilde = key.getParameters().getCurve().decodePoint(C); + + // NOTE: Decoded points are already normalized (i.e in affine form) + ECPoint gTilde = curve.decodePoint(C); // Compute the static-ephemeral key agreement - ECPoint gHat; + ECPoint gHat = gTilde; if ((CofactorMode) || (OldCofactorMode)) { - gHat = gTilde.multiply(h); - } - else - { - gHat = gTilde; + gHat = gHat.multiply(h); } - BigInteger xHat; + BigInteger xHat = ecPrivKey.getD(); if (CofactorMode) { - xHat = ((ECPrivateKeyParameters)key).getD().multiply(h.modInverse(n)).mod(n); - } - else - { - xHat = ((ECPrivateKeyParameters)key).getD(); + xHat = xHat.multiply(h.modInverse(n)).mod(n); } - ECPoint hTilde = gHat.multiply(xHat); + ECPoint hTilde = gHat.multiply(xHat).normalize(); // Encode the shared secret value - int PEHlen = (key.getParameters().getCurve().getFieldSize() + 7) / 8; - byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger()); + byte[] PEH = hTilde.getAffineXCoord().getEncoded(); // Initialise the KDF byte[] kdfInput; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html deleted file mode 100644 index a5174b3..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -The Key Encapsulation Mechanisms (KEMs) from ISO 18033-2. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java index 8a3b5bb..1aa8ede 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java @@ -5,6 +5,7 @@ import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.paddings.ISO7816d4Padding; +import org.bouncycastle.crypto.params.KeyParameter; /** * CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html @@ -103,25 +104,36 @@ public class CMac implements Mac return cipher.getAlgorithmName(); } - private static byte[] doubleLu(byte[] in) + private static int shiftLeft(byte[] block, byte[] output) { - int FirstBit = (in[0] & 0xFF) >> 7; - byte[] ret = new byte[in.length]; - for (int i = 0; i < in.length - 1; i++) - { - ret[i] = (byte)((in[i] << 1) + ((in[i + 1] & 0xFF) >> 7)); - } - ret[in.length - 1] = (byte)(in[in.length - 1] << 1); - if (FirstBit == 1) + int i = block.length; + int bit = 0; + while (--i >= 0) { - ret[in.length - 1] ^= in.length == 16 ? CONSTANT_128 : CONSTANT_64; + int b = block[i] & 0xff; + output[i] = (byte)((b << 1) | bit); + bit = (b >>> 7) & 1; } + return bit; + } + + private static byte[] doubleLu(byte[] in) + { + byte[] ret = new byte[in.length]; + int carry = shiftLeft(in, ret); + int xor = 0xff & (in.length == 16 ? CONSTANT_128 : CONSTANT_64); + + /* + * NOTE: This construction is an attempt at a constant-time implementation. + */ + ret[in.length - 1] ^= (xor >>> ((1 - carry) << 3)); + return ret; } public void init(CipherParameters params) { - if (params != null) + if (params instanceof KeyParameter) { cipher.init(true, params); @@ -130,6 +142,10 @@ public class CMac implements Mac cipher.processBlock(ZEROES, 0, L, 0); Lu = doubleLu(L); Lu2 = doubleLu(Lu); + } else if (params != null) + { + // CMAC mode does not permit IV to underlying CBC mode + throw new IllegalArgumentException("CMac mode only permits key to be set."); } reset(); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java new file mode 100644 index 0000000..150eb61 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java @@ -0,0 +1,279 @@ +package org.bouncycastle.crypto.macs; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.crypto.util.Pack; + +/** + * Poly1305 message authentication code, designed by D. J. Bernstein. + * <p> + * 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. + * <p> + * The polynomial calculation in this implementation is adapted from the public domain <a + * href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> 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/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java new file mode 100644 index 0000000..6097247 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java @@ -0,0 +1,119 @@ +package org.bouncycastle.crypto.macs; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.digests.SkeinEngine; +import org.bouncycastle.crypto.engines.ThreefishEngine; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.SkeinParameters; + +/** + * Implementation of the Skein parameterised MAC function in 256, 512 and 1024 bit block sizes, + * based on the {@link ThreefishEngine Threefish} tweakable block cipher. + * <p/> + * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3 + * competition in October 2010. + * <p/> + * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir + * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker. + * <p/> + * + * @see SkeinEngine + * @see SkeinParameters + */ +public class SkeinMac + implements Mac +{ + /** + * 256 bit block size - Skein MAC-256 + */ + public static final int SKEIN_256 = SkeinEngine.SKEIN_256; + /** + * 512 bit block size - Skein MAC-512 + */ + public static final int SKEIN_512 = SkeinEngine.SKEIN_512; + /** + * 1024 bit block size - Skein MAC-1024 + */ + public static final int SKEIN_1024 = SkeinEngine.SKEIN_1024; + + private SkeinEngine engine; + + /** + * Constructs a Skein MAC with an internal state size and output size. + * + * @param stateSizeBits the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or + * {@link #SKEIN_1024}. + * @param digestSizeBits the output/MAC size to produce in bits, which must be an integral number of bytes. + */ + public SkeinMac(int stateSizeBits, int digestSizeBits) + { + this.engine = new SkeinEngine(stateSizeBits, digestSizeBits); + } + + public SkeinMac(SkeinMac mac) + { + this.engine = new SkeinEngine(mac.engine); + } + + public String getAlgorithmName() + { + return "Skein-MAC-" + (engine.getBlockSize() * 8) + "-" + (engine.getOutputSize() * 8); + } + + /** + * Initialises the Skein digest with the provided parameters.<br> + * See {@link SkeinParameters} for details on the parameterisation of the Skein hash function. + * + * @param params an instance of {@link SkeinParameters} or {@link KeyParameter}. + */ + public void init(CipherParameters params) + throws IllegalArgumentException + { + SkeinParameters skeinParameters; + if (params instanceof SkeinParameters) + { + skeinParameters = (SkeinParameters)params; + } + else if (params instanceof KeyParameter) + { + skeinParameters = new SkeinParameters.Builder().setKey(((KeyParameter)params).getKey()).build(); + } + else + { + throw new IllegalArgumentException("Invalid parameter passed to Skein MAC init - " + + params.getClass().getName()); + } + if (skeinParameters.getKey() == null) + { + throw new IllegalArgumentException("Skein MAC requires a key parameter."); + } + engine.init(skeinParameters); + } + + public int getMacSize() + { + return engine.getOutputSize(); + } + + public void reset() + { + engine.reset(); + } + + public void update(byte in) + { + engine.update(in); + } + + public void update(byte[] in, int inOff, int len) + { + engine.update(in, inOff, len); + } + + public int doFinal(byte[] out, int outOff) + { + return engine.doFinal(out, outOff); + } + +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html deleted file mode 100644 index 0b1f86d..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Classes for creating MACs and HMACs. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java index 9a6e2e0..fef51fd 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java @@ -29,8 +29,8 @@ public class CCMBlockCipher private int macSize; private CipherParameters keyParam; private byte[] macBlock; - private ByteArrayOutputStream associatedText = new ByteArrayOutputStream(); - private ByteArrayOutputStream data = new ByteArrayOutputStream(); + private ExposedByteArrayOutputStream associatedText = new ExposedByteArrayOutputStream(); + private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream(); /** * Basic constructor. @@ -65,6 +65,7 @@ public class CCMBlockCipher { this.forEncryption = forEncryption; + CipherParameters cipherParameters; if (params instanceof AEADParameters) { AEADParameters param = (AEADParameters)params; @@ -72,7 +73,7 @@ public class CCMBlockCipher nonce = param.getNonce(); initialAssociatedText = param.getAssociatedText(); macSize = param.getMacSize() / 8; - keyParam = param.getKey(); + cipherParameters = param.getKey(); } else if (params instanceof ParametersWithIV) { @@ -81,17 +82,25 @@ public class CCMBlockCipher nonce = param.getIV(); initialAssociatedText = null; macSize = macBlock.length / 2; - keyParam = param.getParameters(); + 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() @@ -129,14 +138,11 @@ public class CCMBlockCipher public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException { - byte[] text = data.toByteArray(); - byte[] enc = processPacket(text, 0, text.length); - - System.arraycopy(enc, 0, out, outOff, enc.length); + int len = processPacket(data.getBuffer(), 0, data.size(), out, outOff); reset(); - return enc.length; + return len; } public void reset() @@ -178,9 +184,55 @@ public class CCMBlockCipher 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) @@ -206,42 +258,52 @@ public class CCMBlockCipher BlockCipher ctrCipher = new SICBlockCipher(cipher); ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv)); - int index = inOff; - int outOff = 0; - byte[] output; + int outputLen; + int inIndex = inOff; + int outIndex = outOff; if (forEncryption) { - output = new byte[inLen + macSize]; + 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 (index < inLen - blockSize) // S1... + while (inIndex < (inOff + inLen - blockSize)) // S1... { - ctrCipher.processBlock(in, index, output, outOff); - outOff += blockSize; - index += blockSize; + ctrCipher.processBlock(in, inIndex, output, outIndex); + outIndex += blockSize; + inIndex += blockSize; } byte[] block = new byte[blockSize]; - System.arraycopy(in, index, block, 0, inLen - index); + System.arraycopy(in, inIndex, block, 0, inLen + inOff - inIndex); ctrCipher.processBlock(block, 0, block, 0); - System.arraycopy(block, 0, output, outOff, inLen - index); + System.arraycopy(block, 0, output, outIndex, inLen + inOff - inIndex); - outOff += inLen - index; - - System.arraycopy(macBlock, 0, output, outOff, output.length - outOff); + System.arraycopy(macBlock, 0, output, outOff + inLen, macSize); } else { - output = new byte[inLen - macSize]; + 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 + inLen - macSize, macBlock, 0, macSize); + System.arraycopy(in, inOff + outputLen, macBlock, 0, macSize); ctrCipher.processBlock(macBlock, 0, macBlock, 0); @@ -250,24 +312,24 @@ public class CCMBlockCipher macBlock[i] = 0; } - while (outOff < output.length - blockSize) + while (inIndex < (inOff + outputLen - blockSize)) { - ctrCipher.processBlock(in, index, output, outOff); - outOff += blockSize; - index += blockSize; + ctrCipher.processBlock(in, inIndex, output, outIndex); + outIndex += blockSize; + inIndex += blockSize; } byte[] block = new byte[blockSize]; - System.arraycopy(in, index, block, 0, output.length - outOff); + System.arraycopy(in, inIndex, block, 0, outputLen - (inIndex - inOff)); ctrCipher.processBlock(block, 0, block, 0); - System.arraycopy(block, 0, output, outOff, output.length - outOff); + System.arraycopy(block, 0, output, outIndex, outputLen - (inIndex - inOff)); byte[] calculatedMacBlock = new byte[blockSize]; - calculateMac(output, 0, output.length, calculatedMacBlock); + calculateMac(output, outOff, outputLen, calculatedMacBlock); if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock)) { @@ -275,7 +337,7 @@ public class CCMBlockCipher } } - return output; + return outputLen; } private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock) @@ -344,8 +406,7 @@ public class CCMBlockCipher } if (associatedText.size() > 0) { - byte[] tmp = associatedText.toByteArray(); - cMac.update(tmp, 0, tmp.length); + cMac.update(associatedText.getBuffer(), 0, associatedText.size()); } extra = (extra + textLength) % 16; @@ -375,4 +436,17 @@ public class CCMBlockCipher { return getAssociatedTextLength() > 0; } + + private class ExposedByteArrayOutputStream + extends ByteArrayOutputStream + { + public ExposedByteArrayOutputStream() + { + } + + public byte[] getBuffer() + { + return this.buf; + } + } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java index d0fb9bb..a885169 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java @@ -4,6 +4,7 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; /** * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. @@ -246,6 +247,16 @@ public class CFBBlockCipher } /** + * 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. */ diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java index b8e5b61..5388b40 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java @@ -22,8 +22,9 @@ public class CTSBlockCipher public CTSBlockCipher( BlockCipher cipher) { - if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher)) + 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"); } @@ -72,7 +73,7 @@ public class CTSBlockCipher } /** - * process a single byte, producing an output block if neccessary. + * 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. @@ -200,60 +201,81 @@ public class CTSBlockCipher if (forEncryption) { - cipher.processBlock(buf, 0, block, 0); - if (bufOff < blockSize) { throw new DataLengthException("need at least one block of input for CTS"); } - 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]; - } + cipher.processBlock(buf, 0, block, 0); - if (cipher instanceof CBCBlockCipher) + if (bufOff > blockSize) { - BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher(); - - c.processBlock(buf, blockSize, out, outOff); + 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 { - cipher.processBlock(buf, blockSize, out, outOff); + System.arraycopy(block, 0, out, outOff, blockSize); } - - System.arraycopy(block, 0, out, outOff + blockSize, len); } else { + if (bufOff < blockSize) + { + throw new DataLengthException("need at least one block of input for CTS"); + } + byte[] lastBlock = new byte[blockSize]; - if (cipher instanceof CBCBlockCipher) + if (bufOff > blockSize) { - BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher(); - - c.processBlock(buf, 0, block, 0); + 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); - } - for (int i = blockSize; i != bufOff; i++) - { - lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]); + System.arraycopy(block, 0, out, outOff, blockSize); } - - System.arraycopy(buf, blockSize, block, 0, len); - - cipher.processBlock(block, 0, out, outOff); - System.arraycopy(lastBlock, 0, out, outOff + blockSize, len); } int offset = bufOff; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java index 4999caa..8f74000 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java @@ -123,16 +123,10 @@ public class EAXBlockCipher mac.update(nonce, 0, nonce.length); mac.doFinal(nonceMac, 0); - tag[blockSize - 1] = hTAG; - mac.update(tag, 0, blockSize); - - if (initialAssociatedText != null) - { - processAADBytes(initialAssociatedText, 0, initialAssociatedText.length); - } - // Same BlockCipher underlies this and the mac, so reuse last key on cipher cipher.init(true, new ParametersWithIV(null, nonceMac)); + + reset(); } private void initCipher() @@ -206,7 +200,7 @@ public class EAXBlockCipher { if (cipherInitialized) { - throw new IllegalStateException("AAD data cannot be added after encryption/decription processing has begun."); + throw new IllegalStateException("AAD data cannot be added after encryption/decryption processing has begun."); } mac.update(in, inOff, len); } @@ -246,6 +240,10 @@ public class EAXBlockCipher 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); @@ -263,6 +261,10 @@ public class EAXBlockCipher } else { + if (extra < macSize) + { + throw new InvalidCipherTextException("data too short"); + } if (extra > macSize) { mac.update(bufBlock, 0, extra - macSize); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java new file mode 100644 index 0000000..887c169 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java @@ -0,0 +1,109 @@ +package org.bouncycastle.crypto.modes; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java index 1178974..0e66cf3 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java @@ -206,6 +206,9 @@ public class GOFBBlockCipher */ public void reset() { + firstStep = true; + N3 = 0; + N4 = 0; System.arraycopy(IV, 0, ofbV, 0, IV.length); cipher.reset(); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java index d4d2910..6dc7148 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java @@ -13,7 +13,7 @@ import org.bouncycastle.util.Arrays; /** * An implementation of the "work in progress" Internet-Draft <a - * href="http://tools.ietf.org/html/draft-irtf-cfrg-ocb-00">The OCB Authenticated-Encryption + * href="http://tools.ietf.org/html/draft-irtf-cfrg-ocb-03">The OCB Authenticated-Encryption * Algorithm</a>, licensed per: * <p/> * <blockquote> <a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for @@ -111,7 +111,6 @@ public class OCBBlockCipher public void init(boolean forEncryption, CipherParameters parameters) throws IllegalArgumentException { - this.forEncryption = forEncryption; this.macBlock = null; @@ -156,23 +155,18 @@ public class OCBBlockCipher N = new byte[0]; } - if (N.length > 16 || (N.length == 16 && (N[0] & 0x80) != 0)) + if (N.length > 15) { - /* - * NOTE: We don't just ignore bit 128 because it would hide from the caller the fact - * that two nonces differing only in bit 128 are not different. - */ - throw new IllegalArgumentException("IV must be no more than 127 bits"); + throw new IllegalArgumentException("IV must be no more than 15 bytes"); } /* * KEY-DEPENDENT INITIALISATION */ - // if keyParam is null we're reusing the last key. - if (keyParameter != null) + if (keyParameter == null) { - // TODO + // TODO If 'keyParameter' is null we're re-using the last key. } // hashCipher always used in forward mode @@ -193,17 +187,10 @@ public class OCBBlockCipher byte[] nonce = new byte[16]; System.arraycopy(N, 0, nonce, nonce.length - N.length, N.length); - if (N.length == 16) - { - nonce[0] &= 0x80; - } - else - { - nonce[15 - N.length] = 1; - } + nonce[0] = (byte)(macSize << 4); + nonce[15 - N.length] |= 1; int bottom = nonce[15] & 0x3F; - // System.out.println("bottom: " + bottom); byte[] Ktop = new byte[16]; nonce[15] &= 0xC0; @@ -314,7 +301,6 @@ public class OCBBlockCipher 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) @@ -334,7 +320,6 @@ public class OCBBlockCipher throws IllegalStateException, InvalidCipherTextException { - /* * For decryption, get the tag from the end of the message */ @@ -483,7 +468,6 @@ public class OCBBlockCipher protected void reset(boolean clearMac) { - hashCipher.reset(); mainCipher.reset(); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java new file mode 100644 index 0000000..b34432a --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java @@ -0,0 +1,269 @@ +package org.bouncycastle.crypto.modes; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.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. + * <p> + * 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. + * </p> + */ +public class OldCTSBlockCipher + 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 OldCTSBlockCipher( + BlockCipher cipher) + { + if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher)) + { + 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 org.bouncycastle.crypto.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 org.bouncycastle.crypto.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 org.bouncycastle.crypto.DataLengthException if there is insufficient space in out for + * the output. + * @exception IllegalStateException if the underlying cipher is not + * initialised. + * @exception org.bouncycastle.crypto.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) + { + cipher.processBlock(buf, 0, block, 0); + + if (bufOff < blockSize) + { + throw new DataLengthException("need at least one block of input for CTS"); + } + + 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 + { + byte[] lastBlock = new byte[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); + } + + int offset = bufOff; + + reset(); + + return offset; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java index 18e612b..4dee63a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java @@ -220,13 +220,13 @@ public class PGPCFBBlockCipher throw new DataLengthException("input buffer too short"); } - if ((outOff + blockSize) > out.length) - { - throw new DataLengthException("output 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++) @@ -258,6 +258,11 @@ public class PGPCFBBlockCipher } 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++) diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java index f2be2fc..fc25810 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java @@ -4,21 +4,21 @@ import org.bouncycastle.util.Arrays; public class BasicGCMExponentiator implements GCMExponentiator { - private byte[] x; + private int[] x; public void init(byte[] x) { - this.x = Arrays.clone(x); + this.x = GCMUtil.asInts(x); } public void exponentiateX(long pow, byte[] output) { // Initial value is little-endian 1 - byte[] y = GCMUtil.oneAsBytes(); + int[] y = GCMUtil.oneAsInts(); if (pow > 0) { - byte[] powX = Arrays.clone(x); + int[] powX = Arrays.clone(x); do { if ((pow & 1L) != 0) @@ -31,6 +31,6 @@ public class BasicGCMExponentiator implements GCMExponentiator while (pow > 0); } - System.arraycopy(y, 0, output, 0, 16); + GCMUtil.asBytes(y, output); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java index 4875301..3031a44 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java @@ -5,6 +5,32 @@ import org.bouncycastle.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]; @@ -15,78 +41,155 @@ abstract class GCMUtil static int[] oneAsInts() { int[] tmp = new int[4]; - tmp[0] = 0x80000000; + tmp[0] = 1 << 31; + return tmp; + } + + static long[] oneAsLongs() + { + long[] tmp = new long[2]; + tmp[0] = 1L << 63; return tmp; } - static byte[] asBytes(int[] ns) + 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) { - byte[] output = new byte[16]; - Pack.intToBigEndian(ns, output, 0); - return output; + Pack.bigEndianToInt(x, 0, z); } - static int[] asInts(byte[] bs) + static long[] asLongs(byte[] x) { - int[] output = new int[4]; - Pack.bigEndianToInt(bs, 0, output); - return output; + long[] z = new long[2]; + Pack.bigEndianToLong(x, 0, z); + return z; } - static void asInts(byte[] bs, int[] output) + static void asLongs(byte[] x, long[] z) { - Pack.bigEndianToInt(bs, 0, output); + Pack.bigEndianToLong(x, 0, z); } - static void multiply(byte[] block, byte[] val) + static void multiply(byte[] x, byte[] y) { - byte[] tmp = Arrays.clone(block); - byte[] c = new byte[16]; + byte[] r0 = Arrays.clone(x); + byte[] r1 = new byte[16]; for (int i = 0; i < 16; ++i) { - byte bits = val[i]; + byte bits = y[i]; for (int j = 7; j >= 0; --j) { if ((bits & (1 << j)) != 0) { - xor(c, tmp); + xor(r1, r0); } - boolean lsb = (tmp[15] & 1) != 0; - shiftRight(tmp); - if (lsb) + if (shiftRight(r0) != 0) { - // R = new byte[]{ 0xe1, ... }; -// GCMUtil.xor(v, R); - tmp[0] ^= (byte)0xe1; + r0[0] ^= E1B; } } } - System.arraycopy(c, 0, block, 0, 16); + 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) { - boolean lsb = (x[3] & 1) != 0; - shiftRight(x); - if (lsb) + if (shiftRight(x) != 0) { - // R = new int[]{ 0xe1000000, 0, 0, 0 }; -// xor(v, R); - x[0] ^= 0xe1000000; + x[0] ^= E1; } } - static void multiplyP(int[] x, int[] output) + static void multiplyP(int[] x, int[] y) { - boolean lsb = (x[3] & 1) != 0; - shiftRight(x, output); - if (lsb) + if (shiftRight(x, y) != 0) { - output[0] ^= 0xe1000000; + y[0] ^= E1; } } @@ -98,163 +201,257 @@ abstract class GCMUtil // multiplyP(x); // } - int lsw = x[3]; - shiftRightN(x, 8); - for (int i = 7; i >= 0; --i) - { - if ((lsw & (1 << i)) != 0) - { - x[0] ^= (0xe1000000 >>> (7 - i)); - } - } + int c = shiftRightN(x, 8); + x[0] ^= LOOKUP[c >>> 24]; } - static void multiplyP8(int[] x, int[] output) + static void multiplyP8(int[] x, int[] y) { - int lsw = x[3]; - shiftRightN(x, 8, output); - for (int i = 7; i >= 0; --i) - { - if ((lsw & (1 << i)) != 0) - { - output[0] ^= (0xe1000000 >>> (7 - i)); - } - } + int c = shiftRightN(x, 8, y); + y[0] ^= LOOKUP[c >>> 24]; } - static void shiftRight(byte[] block) + static byte shiftRight(byte[] x) { - int i = 0; - int bit = 0; - for (;;) +// 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 = block[i] & 0xff; - block[i] = (byte) ((b >>> 1) | bit); - if (++i == 16) - { - break; - } - bit = (b & 1) << 7; + 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 void shiftRight(byte[] block, byte[] output) + static byte shiftRight(byte[] x, byte[] z) { - int i = 0; - int bit = 0; - for (;;) +// 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 = block[i] & 0xff; - output[i] = (byte) ((b >>> 1) | bit); - if (++i == 16) - { - break; - } - bit = (b & 1) << 7; + 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 void shiftRight(int[] block) + static int shiftRight(int[] x) { - int i = 0; - int bit = 0; - for (;;) - { - int b = block[i]; - block[i] = (b >>> 1) | bit; - if (++i == 4) - { - break; - } - bit = b << 31; - } +// 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 void shiftRight(int[] block, int[] output) + static int shiftRight(int[] x, int[] z) { - int i = 0; - int bit = 0; - for (;;) - { - int b = block[i]; - output[i] = (b >>> 1) | bit; - if (++i == 4) - { - break; - } - bit = b << 31; - } +// 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 void shiftRightN(int[] block, int n) + static long shiftRight(long[] x) { - int i = 0; - int bits = 0; - for (;;) - { - int b = block[i]; - block[i] = (b >>> n) | bits; - if (++i == 4) - { - break; - } - bits = b << (32 - n); - } + 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 void shiftRightN(int[] block, int n, int[] output) + 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; - int bits = 0; - for (;;) + do { - int b = block[i]; - output[i] = (b >>> n) | bits; - if (++i == 4) - { - break; - } - bits = b << (32 - n); + 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[] block, byte[] val) + static void xor(byte[] x, byte[] y, int yOff, int yLen) { - for (int i = 15; i >= 0; --i) + while (yLen-- > 0) { - block[i] ^= val[i]; + x[yLen] ^= y[yOff + yLen]; } } - static void xor(byte[] block, byte[] val, int off, int len) + static void xor(byte[] x, byte[] y, byte[] z) { - while (len-- > 0) + int i = 0; + do { - block[len] ^= val[off + len]; + 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(byte[] block, byte[] val, byte[] output) + static void xor(int[] x, int[] y) { - for (int i = 15; i >= 0; --i) - { - output[i] = (byte)(block[i] ^ val[i]); - } + x[0] ^= y[0]; + x[1] ^= y[1]; + x[2] ^= y[2]; + x[3] ^= y[3]; } - static void xor(int[] block, int[] val) + static void xor(int[] x, int[] y, int[] z) { - for (int i = 3; i >= 0; --i) - { - block[i] ^= val[i]; - } + 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(int[] block, int[] val, int[] output) + static void xor(long[] x, long[] y) { - for (int i = 3; i >= 0; --i) - { - output[i] = block[i] ^ val[i]; - } + 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/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java index a051208..6eff4e3 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java @@ -12,31 +12,32 @@ public class Tables1kGCMExponentiator implements GCMExponentiator public void init(byte[] x) { - if (lookupPowX2 != null && Arrays.areEqual(x, (byte[])lookupPowX2.elementAt(0))) + int[] y = GCMUtil.asInts(x); + if (lookupPowX2 != null && Arrays.areEqual(y, (int[])lookupPowX2.elementAt(0))) { return; } lookupPowX2 = new Vector(8); - lookupPowX2.addElement(Arrays.clone(x)); + lookupPowX2.addElement(y); } public void exponentiateX(long pow, byte[] output) { - byte[] y = GCMUtil.oneAsBytes(); + int[] y = GCMUtil.oneAsInts(); int bit = 0; while (pow > 0) { if ((pow & 1L) != 0) { ensureAvailable(bit); - GCMUtil.multiply(y, (byte[])lookupPowX2.elementAt(bit)); + GCMUtil.multiply(y, (int[])lookupPowX2.elementAt(bit)); } ++bit; pow >>>= 1; } - System.arraycopy(y, 0, output, 0, 16); + GCMUtil.asBytes(y, output); } private void ensureAvailable(int bit) @@ -44,7 +45,7 @@ public class Tables1kGCMExponentiator implements GCMExponentiator int count = lookupPowX2.size(); if (count <= bit) { - byte[] tmp = (byte[])lookupPowX2.elementAt(count - 1); + int[] tmp = (int[])lookupPowX2.elementAt(count - 1); do { tmp = Arrays.clone(tmp); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html deleted file mode 100644 index 5402df4..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Modes for symmetric ciphers. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/package.html deleted file mode 100644 index ee5487f..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Base classes for the lightweight API. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html deleted file mode 100644 index 2b82e60..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Paddings for symmetric ciphers. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java index 05a1327..9cc6e72 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java @@ -41,7 +41,7 @@ public class ECDomainParameters byte[] seed) { this.curve = curve; - this.G = G; + this.G = G.normalize(); this.n = n; this.h = h; this.seed = seed; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java index 5fbea19..b6b3fb6 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java @@ -12,7 +12,7 @@ public class ECPublicKeyParameters ECDomainParameters params) { super(false, params); - this.Q = Q; + this.Q = Q.normalize(); } public ECPoint getQ() diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java new file mode 100644 index 0000000..0eb6cb7 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java @@ -0,0 +1,52 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java new file mode 100644 index 0000000..383678a --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java @@ -0,0 +1,80 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java new file mode 100644 index 0000000..8a6e7f5 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java @@ -0,0 +1,96 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.DerivationParameters; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java new file mode 100644 index 0000000..76241ee --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java @@ -0,0 +1,293 @@ +package org.bouncycastle.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.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.digests.SkeinDigest; +import org.bouncycastle.crypto.digests.SkeinEngine; +import org.bouncycastle.crypto.macs.SkeinMac; +import org.bouncycastle.util.Integers; + +/** + * Parameters for the Skein hash function - a series of byte[] strings identified by integer tags. + * <p/> + * Parameterised Skein can be used for: + * <ul> + * <li>MAC generation, by providing a {@link SkeinParameters.Builder#setKey(byte[]) key}.</li> + * <li>Randomised hashing, by providing a {@link SkeinParameters.Builder#setNonce(byte[]) nonce}.</li> + * <li>A hash function for digital signatures, associating a + * {@link SkeinParameters.Builder#setPublicKey(byte[]) public key} with the message digest.</li> + * <li>A key derivation function, by providing a + * {@link SkeinParameters.Builder#setKeyIdentifier(byte[]) key identifier}.</li> + * <li>Personalised hashing, by providing a + * {@link SkeinParameters.Builder#setPersonalisation(Date, String, String) recommended format} or + * {@link SkeinParameters.Builder#setPersonalisation(byte[]) arbitrary} personalisation string.</li> + * </ul> + * + * @see SkeinEngine + * @see SkeinDigest + * @see SkeinMac + */ +public class SkeinParameters + implements CipherParameters +{ + /** + * The parameter type for a secret key, supporting MAC or KDF functions: {@value + * #PARAM_TYPE_KEY}. + */ + public static final int PARAM_TYPE_KEY = 0; + + /** + * The parameter type for the Skein configuration block: {@value #PARAM_TYPE_CONFIG}. + */ + public static final int PARAM_TYPE_CONFIG = 4; + + /** + * The parameter type for a personalisation string: {@value #PARAM_TYPE_PERSONALISATION}. + */ + public static final int PARAM_TYPE_PERSONALISATION = 8; + + /** + * The parameter type for a public key: {@value #PARAM_TYPE_PUBLIC_KEY}. + */ + public static final int PARAM_TYPE_PUBLIC_KEY = 12; + + /** + * The parameter type for a key identifier string: {@value #PARAM_TYPE_KEY_IDENTIFIER}. + */ + public static final int PARAM_TYPE_KEY_IDENTIFIER = 16; + + /** + * The parameter type for a nonce: {@value #PARAM_TYPE_NONCE}. + */ + public static final int PARAM_TYPE_NONCE = 20; + + /** + * The parameter type for the message: {@value #PARAM_TYPE_MESSAGE}. + */ + public static final int PARAM_TYPE_MESSAGE = 48; + + /** + * The parameter type for the output transformation: {@value #PARAM_TYPE_OUTPUT}. + */ + public static final int PARAM_TYPE_OUTPUT = 63; + + private Hashtable parameters; + + public SkeinParameters() + { + this(new Hashtable()); + } + + private SkeinParameters(final Hashtable parameters) + { + this.parameters = parameters; + } + + /** + * Obtains a map of type (Integer) to value (byte[]) for the parameters tracked in this object. + */ + public Hashtable getParameters() + { + return parameters; + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_KEY key parameter}, or <code>null</code> if not + * set. + */ + public byte[] getKey() + { + return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY)); + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_PERSONALISATION personalisation parameter}, or + * <code>null</code> if not set. + */ + public byte[] getPersonalisation() + { + return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PERSONALISATION)); + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_PUBLIC_KEY public key parameter}, or + * <code>null</code> if not set. + */ + public byte[] getPublicKey() + { + return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PUBLIC_KEY)); + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_KEY_IDENTIFIER key identifier parameter}, or + * <code>null</code> if not set. + */ + public byte[] getKeyIdentifier() + { + return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY_IDENTIFIER)); + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_NONCE nonce parameter}, or <code>null</code> if + * not set. + */ + public byte[] getNonce() + { + return (byte[])parameters.get(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.<br> + * Parameter types must be in the range 0,5..62, and cannot use the value {@value + * SkeinParameters#PARAM_TYPE_MESSAGE} (reserved for message body). + * <p/> + * Parameters with type < {@value SkeinParameters#PARAM_TYPE_MESSAGE} are processed before + * the message content, parameters with type > {@value SkeinParameters#PARAM_TYPE_MESSAGE} + * are processed after the message and prior to output. + * + * @param type the type of the parameter, in the range 5..62. + * @param value the byte sequence of the parameter. + * @return + */ + public Builder set(int type, byte[] value) + { + if (value == null) + { + throw new IllegalArgumentException("Parameter value must not be null."); + } + if ((type != PARAM_TYPE_KEY) + && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE)) + { + throw new IllegalArgumentException("Parameter types must be in the range 0,5..47,49..62."); + } + if (type == PARAM_TYPE_CONFIG) + { + throw new IllegalArgumentException("Parameter type " + PARAM_TYPE_CONFIG + + " is reserved for internal use."); + } + this.parameters.put(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. + * <p/> + * The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte + * sequence using UTF-8 encoding. + * + * @param date the date the personalised application of the Skein was defined. + * @param emailAddress the email address of the creation of the personalised application. + * @param distinguisher an arbitrary personalisation string distinguishing the application. + * @return + */ + public Builder setPersonalisation(Date date, String emailAddress, String distinguisher) + { + try + { + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + final OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8"); + final DateFormat format = new SimpleDateFormat("YYYYMMDD"); + out.write(format.format(date)); + out.write(" "); + out.write(emailAddress); + out.write(" "); + out.write(distinguisher); + out.close(); + return set(PARAM_TYPE_PERSONALISATION, bout.toByteArray()); + } + catch (IOException e) + { + throw new IllegalStateException("Byte I/O failed: " + e); + } + } + + /** + * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter. + */ + public Builder setPublicKey(byte[] publicKey) + { + return set(PARAM_TYPE_PUBLIC_KEY, publicKey); + } + + /** + * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter. + */ + public Builder setKeyIdentifier(byte[] keyIdentifier) + { + return set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier); + } + + /** + * Sets the {@link SkeinParameters#PARAM_TYPE_NONCE} parameter. + */ + public Builder setNonce(byte[] nonce) + { + return set(PARAM_TYPE_NONCE, nonce); + } + + /** + * Constructs a new {@link SkeinParameters} instance with the parameters provided to this + * builder. + */ + public SkeinParameters build() + { + return new SkeinParameters(parameters); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java new file mode 100644 index 0000000..fa16fac --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java @@ -0,0 +1,40 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.util.Arrays; + +/** + * Parameters for tweakable block ciphers. + */ +public class TweakableBlockCipherParameters + implements CipherParameters +{ + private final byte[] tweak; + private final KeyParameter key; + + public TweakableBlockCipherParameters(final KeyParameter key, final byte[] tweak) + { + this.key = key; + this.tweak = Arrays.clone(tweak); + } + + /** + * Gets the key. + * + * @return the key to use, or <code>null</code> to use the current key. + */ + public KeyParameter getKey() + { + return key; + } + + /** + * Gets the tweak value. + * + * @return the tweak to use, or <code>null</code> to use the current tweak. + */ + public byte[] getTweak() + { + return tweak; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html deleted file mode 100644 index 4e00a75..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Classes for parameter objects for ciphers and generators. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java index 209b5e2..3245eab 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java @@ -4,6 +4,9 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.SecureRandom; +/** + * A secure random that returns pre-seeded data to calls of nextBytes() or generateSeed(). + */ public class FixedSecureRandom extends SecureRandom { @@ -70,7 +73,16 @@ public class FixedSecureRandom _index += bytes.length; } - + + public byte[] generateSeed(int numBytes) + { + byte[] bytes = new byte[numBytes]; + + this.nextBytes(bytes); + + return bytes; + } + // // classpath's implementation of SecureRandom doesn't currently go back to nextBytes // when next is called. We can't override next as it's a final method. diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java index 66f05c5..59ea101 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java @@ -6,6 +6,7 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG; +import org.bouncycastle.crypto.prng.drbg.DualECPoints; import org.bouncycastle.crypto.prng.drbg.DualECSP800DRBG; import org.bouncycastle.crypto.prng.drbg.HMacSP800DRBG; import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG; @@ -144,7 +145,7 @@ public class SP800SecureRandomBuilder } /** - * Build a SecureRandom based on a SP 800-90A Dual EC DRBG. + * Build a SecureRandom based on a SP 800-90A Dual EC DRBG using the NIST point set. * * @param digest digest algorithm to use in the DRBG underneath the SecureRandom. * @param nonce nonce value to use in DRBG construction. @@ -156,6 +157,21 @@ public class SP800SecureRandomBuilder return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new DualECDRBGProvider(digest, nonce, personalizationString, securityStrength), predictionResistant); } + /** + * Build a SecureRandom based on a SP 800-90A Dual EC DRBG according to a defined point set. + * + * @param pointSet an array of DualECPoints to use for DRB generation. + * @param digest digest algorithm to use in the DRBG underneath the SecureRandom. + * @param nonce nonce value to use in DRBG construction. + * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes. + * @return a SecureRandom supported by a Dual EC DRBG. + */ + public SP800SecureRandom buildDualEC(DualECPoints[] pointSet, Digest digest, byte[] nonce, boolean predictionResistant) + { + return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new ConfigurableDualECDRBGProvider(pointSet, digest, nonce, personalizationString, securityStrength), predictionResistant); + } + + private static class HashDRBGProvider implements DRBGProvider { @@ -200,6 +216,31 @@ public class SP800SecureRandomBuilder } } + private static class ConfigurableDualECDRBGProvider + implements DRBGProvider + { + private final DualECPoints[] pointSet; + private final Digest digest; + private final byte[] nonce; + private final byte[] personalizationString; + private final int securityStrength; + + public ConfigurableDualECDRBGProvider(DualECPoints[] pointSet, Digest digest, byte[] nonce, byte[] personalizationString, int securityStrength) + { + this.pointSet = new DualECPoints[pointSet.length]; + System.arraycopy(pointSet, 0, this.pointSet, 0, pointSet.length); + this.digest = digest; + this.nonce = nonce; + this.personalizationString = personalizationString; + this.securityStrength = securityStrength; + } + + public SP80090DRBG get(EntropySource entropySource) + { + return new DualECSP800DRBG(pointSet, digest, securityStrength, entropySource, personalizationString, nonce); + } + } + private static class HMacDRBGProvider implements DRBGProvider { diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java new file mode 100644 index 0000000..c3715bc --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java @@ -0,0 +1,82 @@ +package org.bouncycastle.crypto.prng.drbg; + +import org.bouncycastle.math.ec.ECPoint; + +/** + * General class for providing point pairs for use with DualEC DRBG. See NIST SP 800-90A for further details. + */ +public class DualECPoints +{ + private final ECPoint p; + private final ECPoint q; + private final int securityStrength; + private final int cofactor; + + /** + * Base Constructor. + * <p> + * The cofactor is used to calculate the output block length (maxOutlen) according to + * <pre> + * max_outlen = largest multiple of 8 less than ((field size in bits) - (13 + log2(cofactor)) + * </pre> + * </p> + * @param securityStrength maximum security strength to be associated with these parameters + * @param p the P point. + * @param q the Q point. + * @param cofactor cofactor associated with the domain parameters for the point generation. + */ + public DualECPoints(int securityStrength, ECPoint p, ECPoint q, int cofactor) + { + if (!p.getCurve().equals(q.getCurve())) + { + throw new IllegalArgumentException("points need to be on the same curve"); + } + + this.securityStrength = securityStrength; + this.p = p; + this.q = q; + this.cofactor = cofactor; + } + + public int getSeedLen() + { + return p.getCurve().getFieldSize(); + } + + public int getMaxOutlen() + { + return ((p.getCurve().getFieldSize() - (13 + log2(cofactor))) / 8) * 8; + } + + public ECPoint getP() + { + return p; + } + + public ECPoint getQ() + { + return q; + } + + public int getSecurityStrength() + { + return securityStrength; + } + + public int getCofactor() + { + return cofactor; + } + + private static int log2(int value) + { + int log = 0; + + while ((value >>= 1) != 0) + { + log++; + } + + return log; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java index 3cee39c..8d326ff 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java @@ -6,7 +6,6 @@ import org.bouncycastle.asn1.nist.NISTNamedCurves; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.math.ec.ECCurve; -import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; @@ -35,6 +34,26 @@ public class DualECSP800DRBG private static final BigInteger p521_Qx = new BigInteger("1b9fa3e518d683c6b65763694ac8efbaec6fab44f2276171a42726507dd08add4c3b3f4c1ebc5b1222ddba077f722943b24c3edfa0f85fe24d0c8c01591f0be6f63", 16); private static final BigInteger p521_Qy = new BigInteger("1f3bdba585295d9a1110d1df1f9430ef8442c5018976ff3437ef91b81dc0b8132c8d5c39c32d0e004a3092b7d327c0e7a4d26d2c7b69b58f9066652911e457779de", 16); + private static final DualECPoints[] nistPoints; + + static + { + nistPoints = new DualECPoints[3]; + + ECCurve.Fp curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve(); + + nistPoints[0] = new DualECPoints(128, curve.createPoint(p256_Px, p256_Py), curve.createPoint(p256_Qx, p256_Qy), 1); + + curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve(); + + nistPoints[1] = new DualECPoints(192, curve.createPoint(p384_Px, p384_Py), curve.createPoint(p384_Qx, p384_Qy), 1); + + curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve(); + + nistPoints[2] = new DualECPoints(256, curve.createPoint(p521_Px, p521_Py), curve.createPoint(p521_Qx, p521_Qy), 1); + } + + private static final long RESEED_MAX = 1L << (32 - 1); private static final int MAX_ADDITIONAL_INPUT = 1 << (13 - 1); private static final int MAX_ENTROPY_LENGTH = 1 << (13 - 1); @@ -65,6 +84,23 @@ public class DualECSP800DRBG */ 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. + * <p> + * Minimum entropy requirement is the security strength requested. + * </p> + * @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; @@ -82,43 +118,23 @@ public class DualECSP800DRBG byte[] entropy = entropySource.getEntropy(); byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString); - if (securityStrength <= 128) - { - if (Utils.getMaxSecurityStrength(digest) < 128) - { - throw new IllegalArgumentException("Requested security strength is not supported by digest"); - } - _seedlen = 256; - _outlen = 240 / 8; - _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve(); - _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Px), new ECFieldElement.Fp(_curve.getQ(), p256_Py)); - _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Qx), new ECFieldElement.Fp(_curve.getQ(), p256_Qy)); - } - else if (securityStrength <= 192) + for (int i = 0; i != pointSet.length; i++) { - if (Utils.getMaxSecurityStrength(digest) < 192) + if (securityStrength <= pointSet[i].getSecurityStrength()) { - throw new IllegalArgumentException("Requested security strength is not supported by digest"); + 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; } - _seedlen = 384; - _outlen = 368 / 8; - _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve(); - _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Px), new ECFieldElement.Fp(_curve.getQ(), p384_Py)); - _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Qx), new ECFieldElement.Fp(_curve.getQ(), p384_Qy)); } - else if (securityStrength <= 256) - { - if (Utils.getMaxSecurityStrength(digest) < 256) - { - throw new IllegalArgumentException("Requested security strength is not supported by digest"); - } - _seedlen = 521; - _outlen = 504 / 8; - _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve(); - _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Px), new ECFieldElement.Fp(_curve.getQ(), p521_Py)); - _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Qx), new ECFieldElement.Fp(_curve.getQ(), p521_Qy)); - } - else + + if (_P == null) { throw new IllegalArgumentException("security strength cannot be greater than 256 bits"); } @@ -159,50 +175,69 @@ public class DualECSP800DRBG 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++) { - BigInteger t = new BigInteger(1, xor(_s, additionalInput)); - - _s = _P.multiply(t).getX().toBigInteger().toByteArray(); + s = getScalarMultipleXCoord(_P, s); //System.err.println("S: " + new String(Hex.encode(_s))); - byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray(); + byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray(); if (r.length > _outlen) { - System.arraycopy(r, r.length - _outlen, output, i * _outlen, _outlen); + System.arraycopy(r, r.length - _outlen, output, outOffset, _outlen); } else { - System.arraycopy(r, 0, output, i * _outlen + (_outlen - r.length), r.length); + System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), r.length); } //System.err.println("R: " + new String(Hex.encode(r))); - additionalInput = null; + outOffset += _outlen; _reseedCounter++; } - if (m * _outlen < output.length) + if (outOffset < output.length) { - BigInteger t = new BigInteger(1, xor(_s, additionalInput)); + s = getScalarMultipleXCoord(_P, s); - _s = _P.multiply(t).getX().toBigInteger().toByteArray(); + byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray(); - byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray(); + int required = output.length - outOffset; - System.arraycopy(r, 0, output, m * _outlen, output.length - (m * _outlen)); + 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(new BigInteger(1, _s)).getX().toBigInteger()); + _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(s).normalize().getAffineXCoord().toBigInteger()); return numberOfBits; } @@ -264,4 +299,9 @@ public class DualECSP800DRBG return s; } + + private BigInteger getScalarMultipleXCoord(ECPoint p, BigInteger s) + { + return p.multiply(s).normalize().getAffineXCoord().toBigInteger(); + } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html deleted file mode 100644 index 630809b..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -NIST Deterministic Random Bit Generators (SP 800-90A). -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html deleted file mode 100644 index 9ad3854..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Lightweight psuedo-random number generators and SecureRandom variants and builders. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java new file mode 100644 index 0000000..fced06e --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java @@ -0,0 +1,41 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java index a96cef0..292c408 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java @@ -1,5 +1,8 @@ package org.bouncycastle.crypto.signers; +import java.math.BigInteger; +import java.security.SecureRandom; + import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DSA; import org.bouncycastle.crypto.params.DSAKeyParameters; @@ -8,9 +11,6 @@ import org.bouncycastle.crypto.params.DSAPrivateKeyParameters; import org.bouncycastle.crypto.params.DSAPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; -import java.math.BigInteger; -import java.security.SecureRandom; - /** * The Digital Signature Algorithm - as described in "Handbook of Applied * Cryptography", pages 452 - 453. @@ -18,9 +18,28 @@ import java.security.SecureRandom; public class DSASigner implements DSA { - DSAKeyParameters key; + private final DSAKCalculator kCalculator; - SecureRandom random; + 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, @@ -59,14 +78,17 @@ public class DSASigner { DSAParameters params = key.getParameters(); BigInteger m = calculateE(params.getQ(), message); - BigInteger k; - int qBitLength = params.getQ().bitLength(); - do + if (kCalculator.isDeterministic()) { - k = new BigInteger(qBitLength, random); + kCalculator.init(params.getQ(), ((DSAPrivateKeyParameters)key).getX(), message); } - while (k.compareTo(params.getQ()) >= 0); + else + { + kCalculator.init(params.getQ(), random); + } + + BigInteger k = kCalculator.nextK(); BigInteger r = params.getG().modPow(k, params.getP()).mod(params.getQ()); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java index a8fc194..0e76950 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java @@ -5,6 +5,7 @@ import java.security.SecureRandom; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DSA; +import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECKeyParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; @@ -56,12 +57,17 @@ public class DSTU4145Signer public BigInteger[] generateSignature(byte[] message) { - ECFieldElement h = hash2FieldElement(key.getParameters().getCurve(), message); - if (h.toBigInteger().signum() == 0) + ECDomainParameters parameters = key.getParameters(); + + ECCurve curve = parameters.getCurve(); + + ECFieldElement h = hash2FieldElement(curve, message); + if (h.isZero()) { - h = key.getParameters().getCurve().fromBigInteger(ONE); + h = curve.fromBigInteger(ONE); } + BigInteger n = parameters.getN(); BigInteger e, r, s; ECFieldElement Fe, y; @@ -71,17 +77,17 @@ public class DSTU4145Signer { do { - e = generateRandomInteger(key.getParameters().getN(), random); - Fe = key.getParameters().getG().multiply(e).getX(); + e = generateRandomInteger(n, random); + Fe = parameters.getG().multiply(e).normalize().getAffineXCoord(); } - while (Fe.toBigInteger().signum() == 0); + while (Fe.isZero()); y = h.multiply(Fe); - r = fieldElement2Integer(key.getParameters().getN(), y); + r = fieldElement2Integer(n, y); } while (r.signum() == 0); - s = r.multiply(((ECPrivateKeyParameters)key).getD()).add(e).mod(key.getParameters().getN()); + s = r.multiply(((ECPrivateKeyParameters)key).getD()).add(e).mod(n); } while (s.signum() == 0); @@ -90,22 +96,28 @@ public class DSTU4145Signer public boolean verifySignature(byte[] message, BigInteger r, BigInteger s) { - if (r.signum() == 0 || s.signum() == 0) + if (r.signum() <= 0 || s.signum() <= 0) { return false; } - if (r.compareTo(key.getParameters().getN()) >= 0 || s.compareTo(key.getParameters().getN()) >= 0) + + ECDomainParameters parameters = key.getParameters(); + + BigInteger n = parameters.getN(); + if (r.compareTo(n) >= 0 || s.compareTo(n) >= 0) { return false; } - ECFieldElement h = hash2FieldElement(key.getParameters().getCurve(), message); - if (h.toBigInteger().signum() == 0) + ECCurve curve = parameters.getCurve(); + + ECFieldElement h = hash2FieldElement(curve, message); + if (h.isZero()) { - h = key.getParameters().getCurve().fromBigInteger(ONE); + h = curve.fromBigInteger(ONE); } - ECPoint R = ECAlgorithms.sumOfTwoMultiplies(key.getParameters().getG(), s, ((ECPublicKeyParameters)key).getQ(), r); + ECPoint R = ECAlgorithms.sumOfTwoMultiplies(parameters.getG(), s, ((ECPublicKeyParameters)key).getQ(), r).normalize(); // components must be bogus. if (R.isInfinity()) @@ -113,8 +125,8 @@ public class DSTU4145Signer return false; } - ECFieldElement y = h.multiply(R.getX()); - return fieldElement2Integer(key.getParameters().getN(), y).compareTo(r) == 0; + ECFieldElement y = h.multiply(R.getAffineXCoord()); + return fieldElement2Integer(n, y).compareTo(r) == 0; } /** @@ -142,7 +154,7 @@ public class DSTU4145Signer byte[] data = Arrays.clone(hash); reverseBytes(data); BigInteger num = new BigInteger(1, data); - while (num.bitLength() >= curve.getFieldSize()) + while (num.bitLength() > curve.getFieldSize()) { num = num.clearBit(num.bitLength() - 1); } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java index a80c574..2a1f98e 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java @@ -19,9 +19,28 @@ import org.bouncycastle.math.ec.ECPoint; public class ECDSASigner implements ECConstants, DSA { - ECKeyParameters key; + private final DSAKCalculator kCalculator; - SecureRandom random; + private ECKeyParameters key; + private SecureRandom random; + + /** + * Default configuration, random K values. + */ + public ECDSASigner() + { + this.kCalculator = new RandomDSAKCalculator(); + } + + /** + * Configuration with an alternate, possibly deterministic calculator of K. + * + * @param kCalculator a K value calculator. + */ + public ECDSASigner(DSAKCalculator kCalculator) + { + this.kCalculator = kCalculator; + } public void init( boolean forSigning, @@ -64,24 +83,28 @@ public class ECDSASigner BigInteger r = null; BigInteger s = null; + if (kCalculator.isDeterministic()) + { + kCalculator.init(n, ((ECPrivateKeyParameters)key).getD(), message); + } + else + { + kCalculator.init(n, random); + } + // 5.3.2 do // generate s { BigInteger k = null; - int nBitLength = n.bitLength(); do // generate r { - do - { - k = new BigInteger(nBitLength, random); - } - while (k.equals(ZERO) || k.compareTo(n) >= 0); + k = kCalculator.nextK(); - ECPoint p = key.getParameters().getG().multiply(k); + ECPoint p = key.getParameters().getG().multiply(k).normalize(); // 5.3.3 - BigInteger x = p.getX().toBigInteger(); + BigInteger x = p.getAffineXCoord().toBigInteger(); r = x.mod(n); } @@ -135,7 +158,7 @@ public class ECDSASigner ECPoint G = key.getParameters().getG(); ECPoint Q = ((ECPublicKeyParameters)key).getQ(); - ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2); + ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2).normalize(); // components must be bogus. if (point.isInfinity()) @@ -143,7 +166,7 @@ public class ECDSASigner return false; } - BigInteger v = point.getX().toBigInteger().mod(n); + BigInteger v = point.getAffineXCoord().toBigInteger().mod(n); return v.equals(r); } @@ -153,17 +176,11 @@ public class ECDSASigner int log2n = n.bitLength(); int messageBitLength = message.length * 8; - if (log2n >= messageBitLength) + BigInteger e = new BigInteger(1, message); + if (log2n < messageBitLength) { - return new BigInteger(1, message); - } - else - { - BigInteger trunc = new BigInteger(1, message); - - trunc = trunc.shiftRight(messageBitLength - log2n); - - return trunc; + e = e.shiftRight(messageBitLength - log2n); } + return e; } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java index 7256d35..f6d7f4f 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java @@ -82,9 +82,9 @@ public class ECGOST3410Signer } while (k.equals(ECConstants.ZERO)); - ECPoint p = key.getParameters().getG().multiply(k); + ECPoint p = key.getParameters().getG().multiply(k).normalize(); - BigInteger x = p.getX().toBigInteger(); + BigInteger x = p.getAffineXCoord().toBigInteger(); r = x.mod(n); } @@ -143,7 +143,7 @@ public class ECGOST3410Signer ECPoint G = key.getParameters().getG(); // P ECPoint Q = ((ECPublicKeyParameters)key).getQ(); - ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, z1, Q, z2); + ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, z1, Q, z2).normalize(); // components must be bogus. if (point.isInfinity()) @@ -151,7 +151,7 @@ public class ECGOST3410Signer return false; } - BigInteger R = point.getX().toBigInteger().mod(n); + BigInteger R = point.getAffineXCoord().toBigInteger().mod(n); return R.equals(r); } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java index 07e8ca7..72bbbcb 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java @@ -101,8 +101,8 @@ public class ECNRSigner // BigInteger Vx = tempPair.getPublic().getW().getAffineX(); ECPublicKeyParameters V = (ECPublicKeyParameters)tempPair.getPublic(); // get temp's public key - BigInteger Vx = V.getQ().getX().toBigInteger(); // get the point's x coordinate - + BigInteger Vx = V.getQ().normalize().getAffineXCoord().toBigInteger(); // get the point's x coordinate + r = Vx.add(e).mod(n); } while (r.equals(ECConstants.ZERO)); @@ -172,7 +172,7 @@ public class ECNRSigner ECPoint G = pubKey.getParameters().getG(); ECPoint W = pubKey.getQ(); // calculate P using Bouncy math - ECPoint P = ECAlgorithms.sumOfTwoMultiplies(G, s, W, r); + ECPoint P = ECAlgorithms.sumOfTwoMultiplies(G, s, W, r).normalize(); // components must be bogus. if (P.isInfinity()) @@ -180,7 +180,7 @@ public class ECNRSigner return false; } - BigInteger x = P.getX().toBigInteger(); + BigInteger x = P.getAffineXCoord().toBigInteger(); BigInteger t = r.subtract(x).mod(n); return t.equals(e); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java new file mode 100644 index 0000000..b96e3f3 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java @@ -0,0 +1,161 @@ +package org.bouncycastle.crypto.signers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; + +/** + * A deterministic K calculator based on the algorithm in section 3.2 of RFC 6979. + */ +public class HMacDSAKCalculator + implements DSAKCalculator +{ + private static final BigInteger ZERO = BigInteger.valueOf(0); + + private final HMac hMac; + private final byte[] K; + private final byte[] V; + + private BigInteger n; + + /** + * Base constructor. + * + * @param digest digest to build the HMAC on. + */ + public HMacDSAKCalculator(Digest digest) + { + this.hMac = new HMac(digest); + this.V = new byte[hMac.getMacSize()]; + this.K = new byte[hMac.getMacSize()]; + } + + public boolean isDeterministic() + { + return true; + } + + public void init(BigInteger n, SecureRandom random) + { + throw new IllegalStateException("Operation not supported"); + } + + public void init(BigInteger n, BigInteger d, byte[] message) + { + this.n = n; + + Arrays.fill(V, (byte)0x01); + Arrays.fill(K, (byte)0); + + byte[] x = new byte[(n.bitLength() + 7) / 8]; + byte[] dVal = BigIntegers.asUnsignedByteArray(d); + + System.arraycopy(dVal, 0, x, x.length - dVal.length, dVal.length); + + byte[] m = new byte[(n.bitLength() + 7) / 8]; + + BigInteger mInt = bitsToInt(message); + + if (mInt.compareTo(n) > 0) + { + mInt = mInt.subtract(n); + } + + byte[] mVal = BigIntegers.asUnsignedByteArray(mInt); + + System.arraycopy(mVal, 0, m, m.length - mVal.length, mVal.length); + + hMac.init(new KeyParameter(K)); + + hMac.update(V, 0, V.length); + hMac.update((byte)0x00); + hMac.update(x, 0, x.length); + hMac.update(m, 0, m.length); + + hMac.doFinal(K, 0); + + hMac.init(new KeyParameter(K)); + + hMac.update(V, 0, V.length); + + hMac.doFinal(V, 0); + + hMac.update(V, 0, V.length); + hMac.update((byte)0x01); + hMac.update(x, 0, x.length); + hMac.update(m, 0, m.length); + + hMac.doFinal(K, 0); + + hMac.init(new KeyParameter(K)); + + hMac.update(V, 0, V.length); + + hMac.doFinal(V, 0); + } + + public BigInteger nextK() + { + byte[] t = new byte[((n.bitLength() + 7) / 8)]; + + for (;;) + { + int tOff = 0; + + while (tOff < t.length) + { + hMac.update(V, 0, V.length); + + hMac.doFinal(V, 0); + + if (t.length - tOff < V.length) + { + System.arraycopy(V, 0, t, tOff, t.length - tOff); + tOff += t.length - tOff; + } + else + { + System.arraycopy(V, 0, t, tOff, V.length); + tOff += V.length; + } + } + + BigInteger k = bitsToInt(t); + + if (k.equals(ZERO) || k.compareTo(n) >= 0) + { + hMac.update(V, 0, V.length); + hMac.update((byte)0x00); + + hMac.doFinal(K, 0); + + hMac.init(new KeyParameter(K)); + + hMac.update(V, 0, V.length); + + hMac.doFinal(V, 0); + } + else + { + return k; + } + } + } + + private BigInteger bitsToInt(byte[] t) + { + BigInteger v = new BigInteger(1, t); + + if (t.length * 8 > n.bitLength()) + { + v = v.shiftRight(t.length * 8 - n.bitLength()); + } + + return v; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java index f33ed31..aaae064 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java @@ -57,9 +57,15 @@ public class RSADigestSigner public RSADigestSigner( Digest digest) { - this.digest = digest; + this(digest, (ASN1ObjectIdentifier)oidMap.get(digest.getAlgorithmName())); + } - algId = new AlgorithmIdentifier((ASN1ObjectIdentifier)oidMap.get(digest.getAlgorithmName()), DERNull.INSTANCE); + public RSADigestSigner( + Digest digest, + ASN1ObjectIdentifier digestOid) + { + this.digest = digest; + this.algId = new AlgorithmIdentifier(digestOid, DERNull.INSTANCE); } /** diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java new file mode 100644 index 0000000..bbd8cda --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java @@ -0,0 +1,43 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html deleted file mode 100644 index 151d3d5..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Basic signers. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java new file mode 100644 index 0000000..ef7f4fb --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java @@ -0,0 +1,7 @@ +package org.bouncycastle.crypto.tls; + +public abstract class AbstractTlsAgreementCredentials + extends AbstractTlsCredentials + implements TlsAgreementCredentials +{ +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java index 9c2a526..71a2cab 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java @@ -5,11 +5,9 @@ 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java index 9e113f9..7d4fd03 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java @@ -8,12 +8,13 @@ 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; @@ -33,6 +34,11 @@ public abstract class AbstractTlsClient 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 @@ -46,7 +52,7 @@ public abstract class AbstractTlsClient // return ProtocolVersion.SSLv3; // "the lowest version number supported by the client" - // return getMinimumServerVersion(); + // return getMinimumVersion(); // "the value of ClientHello.client_version" return getClientVersion(); @@ -54,13 +60,12 @@ public abstract class AbstractTlsClient public ProtocolVersion getClientVersion() { - return ProtocolVersion.TLSv11; + return ProtocolVersion.TLSv12; } public Hashtable getClientExtensions() throws IOException { - Hashtable clientExtensions = null; ProtocolVersion clientVersion = context.getClientVersion(); @@ -71,14 +76,13 @@ public abstract class AbstractTlsClient */ 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}; + 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}; + short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa }; this.supportedSignatureAlgorithms = new Vector(); for (int i = 0; i < hashAlgorithms.length; ++i) @@ -96,14 +100,33 @@ public abstract class AbstractTlsClient this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.dsa)); - if (clientExtensions == null) - { - clientExtensions = new Hashtable(); - } + 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; } @@ -141,19 +164,6 @@ public abstract class AbstractTlsClient this.selectedCompressionMethod = selectedCompressionMethod; } - public void notifySecureRenegotiation(boolean secureRenegotiation) - throws IOException - { - if (!secureRenegotiation) - { - /* - * RFC 5746 3.4. In this case, some clients may want to terminate the handshake instead - * of continuing; see Section 4.1 for discussion. - */ - // throw new TlsFatalAlert(AlertDescription.handshake_failure); - } - } - public void processServerExtensions(Hashtable serverExtensions) throws IOException { @@ -170,6 +180,18 @@ public abstract class AbstractTlsClient { 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); + } } } @@ -210,9 +232,4 @@ public abstract class AbstractTlsClient throws IOException { } - - public void notifyHandshakeComplete() - throws IOException - { - } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java index 1ff67e3..5e02892 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java @@ -5,12 +5,12 @@ 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) @@ -34,7 +34,7 @@ abstract class AbstractTlsContext return clientVersion; } - public void setClientVersion(ProtocolVersion clientVersion) + void setClientVersion(ProtocolVersion clientVersion) { this.clientVersion = clientVersion; } @@ -44,11 +44,21 @@ abstract class AbstractTlsContext return serverVersion; } - public void setServerVersion(ProtocolVersion 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; @@ -61,6 +71,10 @@ abstract class AbstractTlsContext 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(); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java new file mode 100644 index 0000000..b98743f --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java @@ -0,0 +1,6 @@ +package org.bouncycastle.crypto.tls; + +public abstract class AbstractTlsCredentials + implements TlsCredentials +{ +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java new file mode 100644 index 0000000..e6ff39b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java @@ -0,0 +1,7 @@ +package org.bouncycastle.crypto.tls; + +public abstract class AbstractTlsEncryptionCredentials + extends AbstractTlsCredentials + implements TlsEncryptionCredentials +{ +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java index 85057c1..43e80e6 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java @@ -7,7 +7,6 @@ import java.util.Vector; public abstract class AbstractTlsKeyExchange implements TlsKeyExchange { - protected int keyExchange; protected Vector supportedSignatureAlgorithms; @@ -27,7 +26,6 @@ public abstract class AbstractTlsKeyExchange 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: @@ -45,7 +43,6 @@ public abstract class AbstractTlsKeyExchange { switch (keyExchange) { - case KeyExchangeAlgorithm.DH_DSS: case KeyExchangeAlgorithm.DHE_DSS: case KeyExchangeAlgorithm.SRP_DSS: @@ -73,6 +70,12 @@ public abstract class AbstractTlsKeyExchange break; } + case KeyExchangeAlgorithm.DHE_PSK: + case KeyExchangeAlgorithm.ECDHE_PSK: + case KeyExchangeAlgorithm.PSK: + case KeyExchangeAlgorithm.SRP: + break; + default: throw new IllegalStateException("unsupported key exchange algorithm"); } @@ -88,7 +91,6 @@ public abstract class AbstractTlsKeyExchange public void processServerCertificate(Certificate serverCertificate) throws IOException { - if (supportedSignatureAlgorithms == null) { /* diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java index bdfd0d5..80d6af7 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java @@ -1,8 +1,21 @@ package org.bouncycastle.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) { @@ -11,4 +24,8 @@ public abstract class AbstractTlsPeer public void notifyAlertReceived(short alertLevel, short alertDescription) { } + + public void notifyHandshakeComplete() throws IOException + { + } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java index 8235fd1..bd428a9 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java @@ -4,11 +4,12 @@ import java.io.IOException; import java.util.Hashtable; import java.util.Vector; +import org.bouncycastle.util.Arrays; + public abstract class AbstractTlsServer extends AbstractTlsPeer implements TlsServer { - protected TlsCipherFactory cipherFactory; protected TlsServerContext context; @@ -18,6 +19,8 @@ public abstract class AbstractTlsServer protected short[] offeredCompressionMethods; protected Hashtable clientExtensions; + protected short maxFragmentLengthOffered; + protected boolean truncatedHMacOffered; protected Vector supportedSignatureAlgorithms; protected boolean eccCipherSuitesOffered; protected int[] namedCurves; @@ -38,6 +41,16 @@ public abstract class AbstractTlsServer 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() @@ -57,7 +70,6 @@ public abstract class AbstractTlsServer 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) @@ -106,27 +118,15 @@ public abstract class AbstractTlsServer this.offeredCompressionMethods = offeredCompressionMethods; } - public void notifySecureRenegotiation(boolean secureRenegotiation) - throws IOException - { - if (!secureRenegotiation) - { - /* - * RFC 5746 3.6. In this case, some servers may want to terminate the handshake instead - * of continuing; see Section 4.3 for discussion. - */ - throw new TlsFatalAlert(AlertDescription.handshake_failure); - } - } - 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) @@ -176,7 +176,6 @@ public abstract class AbstractTlsServer 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 @@ -197,7 +196,9 @@ public abstract class AbstractTlsServer for (int i = 0; i < cipherSuites.length; ++i) { int cipherSuite = cipherSuites[i]; - if (TlsProtocol.arrayContains(this.offeredCipherSuites, cipherSuite) + + // 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; @@ -212,7 +213,7 @@ public abstract class AbstractTlsServer short[] compressionMethods = getCompressionMethods(); for (int i = 0; i < compressionMethods.length; ++i) { - if (TlsProtocol.arrayContains(offeredCompressionMethods, compressionMethods[i])) + if (Arrays.contains(offeredCompressionMethods, compressionMethods[i])) { return this.selectedCompressionMethod = compressionMethods[i]; } @@ -224,6 +225,15 @@ public abstract class AbstractTlsServer 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)) { @@ -232,15 +242,13 @@ public abstract class AbstractTlsServer * 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}; + this.serverECPointFormats = new short[]{ ECPointFormat.ansiX962_compressed_char2, + ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed }; - this.serverExtensions = new Hashtable(); - TlsECCUtils.addSupportedPointFormatsExtension(serverExtensions, serverECPointFormats); - return serverExtensions; + TlsECCUtils.addSupportedPointFormatsExtension(checkServerExtensions(), serverECPointFormats); } - return null; + return serverExtensions; } public Vector getServerSupplementalData() @@ -249,7 +257,14 @@ public abstract class AbstractTlsServer return null; } + public CertificateStatus getCertificateStatus() + throws IOException + { + return null; + } + public CertificateRequest getCertificateRequest() + throws IOException { return null; } @@ -296,9 +311,4 @@ public abstract class AbstractTlsServer */ return new NewSessionTicket(0L, TlsUtils.EMPTY_BYTES); } - - public void notifyHandshakeComplete() - throws IOException - { - } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java index a0c24c7..3a1d631 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java @@ -1,13 +1,38 @@ package org.bouncycastle.crypto.tls; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java new file mode 100644 index 0000000..3452f52 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java @@ -0,0 +1,11 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java index 5e3269b..91366be 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java @@ -5,11 +5,12 @@ package org.bouncycastle.crypto.tls; */ public class AlertDescription { - /** * This message notifies the recipient that the sender will not send any more messages on this - * connection. The session becomes unresumable if any connection is terminated without proper - * close_notify messages with level equal to warning. + * 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; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java index 8b9d4ab..7642c4a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java @@ -25,12 +25,12 @@ public class ByteQueue /** * The initial size for our buffer. */ - private static final int INITBUFSIZE = 1024; + private static final int DEFAULT_CAPACITY = 1024; /** * The buffer where we store our data. */ - private byte[] databuf = new byte[ByteQueue.INITBUFSIZE]; + private byte[] databuf;; /** * How many bytes at the beginning of the buffer are skipped. @@ -42,6 +42,16 @@ public class ByteQueue */ private int available = 0; + public ByteQueue() + { + this(DEFAULT_CAPACITY); + } + + public ByteQueue(int capacity) + { + databuf = new byte[capacity]; + } + /** * Read data from the buffer. * @@ -62,26 +72,34 @@ public class ByteQueue + " is too small for a read of " + len + " bytes"); } System.arraycopy(databuf, skipped + skip, buf, offset, len); - return; } /** * Add some data to our buffer. * - * @param data A byte-array to read data from. - * @param offset How many bytes to skip at the beginning of the array. - * @param len How many bytes to read from the array. + * @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[] data, int offset, int len) + public void addData(byte[] buf, int off, int len) { if ((skipped + available + len) > databuf.length) { - byte[] tmp = new byte[ByteQueue.nextTwoPow(data.length)]; - System.arraycopy(databuf, skipped, tmp, 0, available); + 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; - databuf = tmp; } - System.arraycopy(data, offset, databuf, skipped + available, len); + + System.arraycopy(buf, off, databuf, skipped + available, len); available += len; } @@ -102,15 +120,27 @@ public class ByteQueue */ available -= i; skipped += i; + } - /* - * If more than half of our data is skipped, we will move the data in the buffer. - */ - if (skipped > (databuf.length / 2)) - { - System.arraycopy(databuf, skipped, databuf, 0, available); - skipped = 0; - } + /** + * 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; } /** diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java new file mode 100644 index 0000000..8902ed7 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java @@ -0,0 +1,15 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java index fab79f4..02cf693 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java @@ -7,7 +7,6 @@ import java.io.OutputStream; import java.util.Vector; import org.bouncycastle.asn1.ASN1Encoding; -import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Primitive; /** @@ -25,7 +24,6 @@ import org.bouncycastle.asn1.ASN1Primitive; */ public class Certificate { - public static final Certificate EMPTY_CHAIN = new Certificate( new org.bouncycastle.asn1.x509.Certificate[0]); @@ -46,7 +44,7 @@ public class Certificate */ public org.bouncycastle.asn1.x509.Certificate[] getCerts() { - return clone(certificateList); + return getCertificateList(); } /** @@ -55,7 +53,7 @@ public class Certificate */ public org.bouncycastle.asn1.x509.Certificate[] getCertificateList() { - return clone(certificateList); + return cloneCertificateList(); } public org.bouncycastle.asn1.x509.Certificate getCertificateAt(int index) @@ -86,21 +84,23 @@ public class Certificate public void encode(OutputStream output) throws IOException { - Vector encCerts = new Vector(this.certificateList.length); + Vector derEncodings = new Vector(this.certificateList.length); + int totalLength = 0; for (int i = 0; i < this.certificateList.length; ++i) { - byte[] encCert = certificateList[i].getEncoded(ASN1Encoding.DER); - encCerts.addElement(encCert); - totalLength += encCert.length + 3; + 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 < encCerts.size(); ++i) + for (int i = 0; i < derEncodings.size(); ++i) { - byte[] encCert = (byte[])encCerts.elementAt(i); - TlsUtils.writeOpaque24(encCert, output); + byte[] derEncoding = (byte[])derEncodings.elementAt(i); + TlsUtils.writeOpaque24(derEncoding, output); } } @@ -114,40 +114,36 @@ public class Certificate public static Certificate parse(InputStream input) throws IOException { - org.bouncycastle.asn1.x509.Certificate[] certs; - int left = TlsUtils.readUint24(input); - if (left == 0) + int totalLength = TlsUtils.readUint24(input); + if (totalLength == 0) { return EMPTY_CHAIN; } - Vector tmp = new Vector(); - while (left > 0) - { - int size = TlsUtils.readUint24(input); - left -= 3 + size; - byte[] buf = TlsUtils.readFully(size, input); + byte[] certListData = TlsUtils.readFully(totalLength, input); - ByteArrayInputStream bis = new ByteArrayInputStream(buf); - ASN1Primitive asn1 = new ASN1InputStream(bis).readObject(); - TlsProtocol.assertEmpty(bis); + ByteArrayInputStream buf = new ByteArrayInputStream(certListData); - tmp.addElement(org.bouncycastle.asn1.x509.Certificate.getInstance(asn1)); + Vector certificate_list = new Vector(); + while (buf.available() > 0) + { + byte[] derEncoding = TlsUtils.readOpaque24(buf); + ASN1Primitive asn1Cert = TlsUtils.readDERObject(derEncoding); + certificate_list.addElement(org.bouncycastle.asn1.x509.Certificate.getInstance(asn1Cert)); } - certs = new org.bouncycastle.asn1.x509.Certificate[tmp.size()]; - for (int i = 0; i < tmp.size(); i++) + + org.bouncycastle.asn1.x509.Certificate[] certificateList = new org.bouncycastle.asn1.x509.Certificate[certificate_list.size()]; + for (int i = 0; i < certificate_list.size(); i++) { - certs[i] = (org.bouncycastle.asn1.x509.Certificate)tmp.elementAt(i); + certificateList[i] = (org.bouncycastle.asn1.x509.Certificate)certificate_list.elementAt(i); } - return new Certificate(certs); + return new Certificate(certificateList); } - private org.bouncycastle.asn1.x509.Certificate[] clone(org.bouncycastle.asn1.x509.Certificate[] list) + protected org.bouncycastle.asn1.x509.Certificate[] cloneCertificateList() { - org.bouncycastle.asn1.x509.Certificate[] rv = new org.bouncycastle.asn1.x509.Certificate[list.length]; - - System.arraycopy(list, 0, rv, 0, rv.length); - - return rv; + org.bouncycastle.asn1.x509.Certificate[] result = new org.bouncycastle.asn1.x509.Certificate[certificateList.length]; + System.arraycopy(certificateList, 0, result, 0, result.length); + return result; } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java index 00bf950..e9606e3 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java @@ -25,8 +25,9 @@ import org.bouncycastle.asn1.x500.X500Name; */ public class CertificateRequest { - private short[] certificateTypes; - private Vector certificateAuthorities; + 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 @@ -37,9 +38,10 @@ public class CertificateRequest * @param certificateTypes see {@link ClientCertificateType} for valid constants. * @param certificateAuthorities a {@link Vector} of {@link X500Name}. */ - public CertificateRequest(short[] certificateTypes, Vector certificateAuthorities) + public CertificateRequest(short[] certificateTypes, Vector supportedSignatureAlgorithms, Vector certificateAuthorities) { this.certificateTypes = certificateTypes; + this.supportedSignatureAlgorithms = supportedSignatureAlgorithms; this.certificateAuthorities = certificateAuthorities; } @@ -53,6 +55,14 @@ public class CertificateRequest } /** + * @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() @@ -69,15 +79,19 @@ public class CertificateRequest public void encode(OutputStream output) throws IOException { - if (certificateTypes == null || certificateTypes.length == 0) { - TlsUtils.writeUint8((short)0, output); + TlsUtils.writeUint8(0, output); } else { - TlsUtils.writeUint8((short)certificateTypes.length, output); - TlsUtils.writeUint8Array(certificateTypes, output); + 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()) @@ -86,22 +100,23 @@ public class CertificateRequest } else { + Vector derEncodings = new Vector(certificateAuthorities.size()); - Vector encDNs = new Vector(certificateAuthorities.size()); int totalLength = 0; for (int i = 0; i < certificateAuthorities.size(); ++i) { - X500Name authorityDN = (X500Name)certificateAuthorities.elementAt(i); - byte[] encDN = authorityDN.getEncoded(ASN1Encoding.DER); - encDNs.addElement(encDN); - totalLength += encDN.length; + 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 < encDNs.size(); ++i) + for (int i = 0; i < derEncodings.size(); ++i) { - byte[] encDN = (byte[])encDNs.elementAt(i); + byte[] encDN = (byte[])derEncodings.elementAt(i); output.write(encDN); } } @@ -109,12 +124,15 @@ public class CertificateRequest /** * Parse a {@link CertificateRequest} from an {@link InputStream}. - * - * @param input the {@link InputStream} to parse from. + * + * @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(InputStream input) + public static CertificateRequest parse(TlsContext context, InputStream input) throws IOException { int numTypes = TlsUtils.readUint8(input); @@ -124,17 +142,23 @@ public class CertificateRequest certificateTypes[i] = TlsUtils.readUint8(input); } - byte[] authorities = TlsUtils.readOpaque16(input); - - Vector authorityDNs = new Vector(); + Vector supportedSignatureAlgorithms = null; + if (TlsUtils.isTLSv12(context)) + { + // TODO Check whether SignatureAlgorithm.anonymous is allowed here + supportedSignatureAlgorithms = TlsUtils.parseSupportedSignatureAlgorithms(false, input); + } - ByteArrayInputStream bis = new ByteArrayInputStream(authorities); + Vector certificateAuthorities = new Vector(); + byte[] certAuthData = TlsUtils.readOpaque16(input); + ByteArrayInputStream bis = new ByteArrayInputStream(certAuthData); while (bis.available() > 0) { - byte[] dnBytes = TlsUtils.readOpaque16(bis); - authorityDNs.addElement(X500Name.getInstance(ASN1Primitive.fromByteArray(dnBytes))); + byte[] derEncoding = TlsUtils.readOpaque16(bis); + ASN1Primitive asn1 = TlsUtils.readDERObject(derEncoding); + certificateAuthorities.addElement(X500Name.getInstance(asn1)); } - return new CertificateRequest(certificateTypes, authorityDNs); + return new CertificateRequest(certificateTypes, supportedSignatureAlgorithms, certificateAuthorities); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java new file mode 100644 index 0000000..34a0284 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java @@ -0,0 +1,105 @@ +package org.bouncycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java new file mode 100644 index 0000000..b947c48 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java @@ -0,0 +1,98 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java new file mode 100644 index 0000000..bfe8298 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java @@ -0,0 +1,9 @@ +package org.bouncycastle.crypto.tls; + +public class CertificateStatusType +{ + /* + * RFC 3546 3.6 + */ + public static final short ocsp = 1; +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java new file mode 100644 index 0000000..aab8908 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java @@ -0,0 +1,133 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java new file mode 100644 index 0000000..a858ded --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java @@ -0,0 +1,6 @@ +package org.bouncycastle.crypto.tls; + +public class ChangeCipherSpec +{ + public static final short change_cipher_spec = 1; +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java index 2979cde..c1e7533 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java @@ -5,7 +5,6 @@ package org.bouncycastle.crypto.tls; */ 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; @@ -201,7 +200,98 @@ public class CipherSuite 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java index 1a48491..43b73bf 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java @@ -8,7 +8,6 @@ import org.bouncycastle.crypto.Digest; class CombinedHash implements TlsHandshakeHash { - protected TlsContext context; protected Digest md5; protected Digest sha1; @@ -31,16 +30,35 @@ class CombinedHash this.context = context; } - public TlsHandshakeHash commit() + public TlsHandshakeHash notifyPRFDetermined() { return this; } - public TlsHandshakeHash fork() + 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.bouncycastle.crypto.Digest#getAlgorithmName() */ @@ -80,7 +98,7 @@ class CombinedHash */ public int doFinal(byte[] out, int outOff) { - if (context != null && context.getServerVersion().isSSL()) + if (context != null && TlsUtils.isSSL(context)) { ssl3Complete(md5, SSL3Mac.IPAD, SSL3Mac.OPAD, 48); ssl3Complete(sha1, SSL3Mac.IPAD, SSL3Mac.OPAD, 40); @@ -102,15 +120,15 @@ class CombinedHash protected void ssl3Complete(Digest d, byte[] ipad, byte[] opad, int padLength) { - byte[] secret = context.getSecurityParameters().masterSecret; + byte[] master_secret = context.getSecurityParameters().masterSecret; - d.update(secret, 0, secret.length); + 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(secret, 0, secret.length); + d.update(master_secret, 0, master_secret.length); d.update(opad, 0, padLength); d.update(tmp, 0, tmp.length); } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java index d814eac..65ed9b6 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java @@ -9,4 +9,5 @@ public class ContentType 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java index 8ccacfb..b3b1abe 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java @@ -13,7 +13,6 @@ import org.bouncycastle.util.Arrays; public class DTLSClientProtocol extends DTLSProtocol { - public DTLSClientProtocol(SecureRandom secureRandom) { super(secureRandom); @@ -22,7 +21,6 @@ public class DTLSClientProtocol public DTLSTransport connect(TlsClient client, DatagramTransport transport) throws IOException { - if (client == null) { throw new IllegalArgumentException("'client' cannot be null"); @@ -43,6 +41,17 @@ public class DTLSClientProtocol 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); @@ -67,7 +76,6 @@ public class DTLSClientProtocol protected DTLSTransport clientHandshake(ClientHandshakeState state, DTLSRecordLayer recordLayer) throws IOException { - SecurityParameters securityParameters = state.clientContext.getSecurityParameters(); DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.clientContext, recordLayer); @@ -76,23 +84,23 @@ public class DTLSClientProtocol DTLSReliableHandshake.Message serverMessage = handshake.receiveMessage(); + while (serverMessage.getType() == HandshakeType.hello_verify_request) { - // NOTE: After receiving a record from the server, we discover the record layer version - ProtocolVersion server_version = recordLayer.getDiscoveredPeerVersion(); + ProtocolVersion recordLayerVersion = recordLayer.resetDiscoveredPeerVersion(); ProtocolVersion client_version = state.clientContext.getClientVersion(); - if (!server_version.isEqualOrEarlierVersionOf(client_version)) + /* + * 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); } - state.clientContext.setServerVersion(server_version); - state.client.notifyServerVersion(server_version); - } - - while (serverMessage.getType() == HandshakeType.hello_verify_request) - { - byte[] cookie = parseHelloVerifyRequest(state.clientContext, serverMessage.getBody()); + byte[] cookie = processHelloVerifyRequest(state, serverMessage.getBody()); byte[] patched = patchClientHelloWithCookie(clientHelloBody, cookie); handshake.resetHandshakeMessagesDigest(); @@ -103,16 +111,24 @@ public class DTLSClientProtocol if (serverMessage.getType() == HandshakeType.server_hello) { + reportServerVersion(state, recordLayer.getDiscoveredPeerVersion()); + processServerHello(state, serverMessage.getBody()); - serverMessage = handshake.receiveMessage(); } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.selectedCipherSuite); + 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 @@ -122,6 +138,48 @@ public class DTLSClientProtocol 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()); @@ -135,9 +193,11 @@ public class DTLSClientProtocol state.keyExchange = state.client.getKeyExchange(); state.keyExchange.init(state.clientContext); + Certificate serverCertificate = null; + if (serverMessage.getType() == HandshakeType.certificate) { - processServerCertificate(state, serverMessage.getBody()); + serverCertificate = processServerCertificate(state, serverMessage.getBody()); serverMessage = handshake.receiveMessage(); } else @@ -146,6 +206,22 @@ public class DTLSClientProtocol 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()); @@ -160,6 +236,14 @@ public class DTLSClientProtocol 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 @@ -179,6 +263,8 @@ public class DTLSClientProtocol throw new TlsFatalAlert(AlertDescription.unexpected_message); } + handshake.getHandshakeHash().sealHashAlgorithms(); + Vector clientSupplementalData = state.client.getClientSupplementalData(); if (clientSupplementalData != null) { @@ -223,25 +309,45 @@ public class DTLSClientProtocol 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 instanceof TlsSignerCredentials) + if (state.clientCredentials != null && state.clientCredentials instanceof TlsSignerCredentials) { + TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials; + /* - * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended - * from TLS 1.2 + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 */ - TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials; - byte[] md5andsha1 = handshake.getCurrentHash(); - byte[] signature = signerCredentials.generateCertificateSignature(md5andsha1); - byte[] certificateVerifyBody = generateCertificateVerify(state, signature); + 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); } - recordLayer.initPendingEpoch(state.client.getCipher()); - // NOTE: Calculated exclusive of the Finished message itself - byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, "client finished", - handshake.getCurrentHash()); + byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.client_finished, + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); handshake.sendMessage(HandshakeType.finished, clientVerifyData); if (state.expectSessionTicket) @@ -258,39 +364,42 @@ public class DTLSClientProtocol } // NOTE: Calculated exclusive of the actual Finished message from the server - byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, "server finished", - handshake.getCurrentHash()); - serverMessage = handshake.receiveMessage(); + byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.server_finished, + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); + processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedServerVerifyData); - if (serverMessage.getType() == HandshakeType.finished) - { - processFinished(serverMessage.getBody(), expectedServerVerifyData); - } - else + handshake.finish(); + + if (state.tlsSession != null) { - throw new TlsFatalAlert(AlertDescription.unexpected_message); - } + state.sessionParameters = new SessionParameters.Builder() + .setCipherSuite(securityParameters.cipherSuite) + .setCompressionAlgorithm(securityParameters.compressionAlgorithm) + .setMasterSecret(securityParameters.masterSecret) + .setPeerCertificate(serverCertificate) + .build(); - handshake.finish(); + 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, byte[] signature) + protected byte[] generateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify) throws IOException { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeOpaque16(signature, buf); + certificateVerify.encode(buf); return buf.toByteArray(); } protected byte[] generateClientHello(ClientHandshakeState state, TlsClient client) throws IOException { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); ProtocolVersion client_version = client.getClientVersion(); @@ -304,8 +413,17 @@ public class DTLSClientProtocol buf.write(state.clientContext.getSecurityParameters().getClientRandom()); - // Session id - TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf); + // 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); @@ -325,32 +443,26 @@ public class DTLSClientProtocol * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the * ClientHello. Including both is NOT RECOMMENDED. */ - boolean noRenegExt = state.clientExtensions == null - || state.clientExtensions.get(TlsProtocol.EXT_RenegotiationInfo) == null; + byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo); + boolean noRenegExt = (null == renegExtData); - int count = state.offeredCipherSuites.length; - if (noRenegExt) - { - // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV - ++count; - } + boolean noSCSV = !Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); - TlsUtils.writeUint16(2 * count, buf); - TlsUtils.writeUint16Array(state.offeredCipherSuites, buf); - - if (noRenegExt) + if (noRenegExt && noSCSV) { - TlsUtils.writeUint16(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, buf); + // 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}; + state.offeredCompressionMethods = new short[]{ CompressionMethod._null }; - TlsUtils.writeUint8((short)state.offeredCompressionMethods.length, buf); - TlsUtils.writeUint8Array(state.offeredCompressionMethods, buf); + TlsUtils.writeUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf); // Extensions if (state.clientExtensions != null) @@ -364,16 +476,29 @@ public class DTLSClientProtocol 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) { /* @@ -385,19 +510,69 @@ public class DTLSClientProtocol ByteArrayInputStream buf = new ByteArrayInputStream(body); - state.certificateRequest = CertificateRequest.parse(buf); + state.certificateRequest = CertificateRequest.parse(state.clientContext, buf); TlsProtocol.assertEmpty(buf); state.keyExchange.validateCertificateRequest(state.certificateRequest); } - protected void processNewSessionTicket(ClientHandshakeState state, byte[] body) + 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); @@ -405,10 +580,9 @@ public class DTLSClientProtocol state.client.notifyNewSessionTicket(newSessionTicket); } - protected void processServerCertificate(ClientHandshakeState state, byte[] body) + protected Certificate processServerCertificate(ClientHandshakeState state, byte[] body) throws IOException { - ByteArrayInputStream buf = new ByteArrayInputStream(body); Certificate serverCertificate = Certificate.parse(buf); @@ -418,34 +592,31 @@ public class DTLSClientProtocol 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); - // TODO Read RFCs for guidance on the expected record layer version number ProtocolVersion server_version = TlsUtils.readVersion(buf); - if (!server_version.equals(state.clientContext.getServerVersion())) - { - throw new TlsFatalAlert(AlertDescription.illegal_parameter); - } + reportServerVersion(state, server_version); securityParameters.serverRandom = TlsUtils.readFully(32, buf); - byte[] sessionID = TlsUtils.readOpaque8(buf); - if (sessionID.length > 32) + state.selectedSessionID = TlsUtils.readOpaque8(buf); + if (state.selectedSessionID.length > 32) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - state.client.notifySessionID(sessionID); + state.client.notifySessionID(state.selectedSessionID); state.selectedCipherSuite = TlsUtils.readUint16(buf); - if (!TlsProtocol.arrayContains(state.offeredCipherSuites, state.selectedCipherSuite) + if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite) || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) { @@ -457,7 +628,7 @@ public class DTLSClientProtocol state.client.notifySelectedCipherSuite(state.selectedCipherSuite); state.selectedCompressionMethod = TlsUtils.readUint8(buf); - if (!TlsProtocol.arrayContains(state.offeredCompressionMethods, state.selectedCompressionMethod)) + if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } @@ -502,7 +673,7 @@ public class DTLSClientProtocol * MUST continue to comply with Section 7.4.1.4 for all other extensions. */ if (!extType.equals(TlsProtocol.EXT_RenegotiationInfo) - && (state.clientExtensions == null || state.clientExtensions.get(extType) == null)) + && null == TlsUtils.getExtensionData(state.clientExtensions, extType)) { /* * RFC 3546 2.3 Note that for all extension types (including those defined in @@ -524,8 +695,8 @@ public class DTLSClientProtocol * When a ServerHello is received, the client MUST check if it includes the * "renegotiation_info" extension: */ - byte[] renegExtValue = (byte[])serverExtensions.get(TlsProtocol.EXT_RenegotiationInfo); - if (renegExtValue != null) + byte[] renegExtData = (byte[])serverExtensions.get(TlsProtocol.EXT_RenegotiationInfo); + if (renegExtData != null) { /* * If the extension is present, set the secure_renegotiation flag to TRUE. The @@ -535,7 +706,7 @@ public class DTLSClientProtocol */ state.secure_renegotiation = true; - if (!Arrays.constantTimeAreEqual(renegExtValue, + if (!Arrays.constantTimeAreEqual(renegExtData, TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) { throw new TlsFatalAlert(AlertDescription.handshake_failure); @@ -543,7 +714,16 @@ public class DTLSClientProtocol } } - state.expectSessionTicket = serverExtensions.containsKey(TlsProtocol.EXT_SessionTicket); + 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); @@ -557,7 +737,6 @@ public class DTLSClientProtocol protected void processServerKeyExchange(ClientHandshakeState state, byte[] body) throws IOException { - ByteArrayInputStream buf = new ByteArrayInputStream(body); state.keyExchange.processServerKeyExchange(buf); @@ -568,37 +747,30 @@ public class DTLSClientProtocol protected void processServerSupplementalData(ClientHandshakeState state, byte[] body) throws IOException { - ByteArrayInputStream buf = new ByteArrayInputStream(body); Vector serverSupplementalData = TlsProtocol.readSupplementalDataMessage(buf); state.client.processServerSupplementalData(serverSupplementalData); } - protected static byte[] parseHelloVerifyRequest(TlsContext context, byte[] body) + protected void reportServerVersion(ClientHandshakeState state, ProtocolVersion server_version) throws IOException { - - ByteArrayInputStream buf = new ByteArrayInputStream(body); - - ProtocolVersion server_version = TlsUtils.readVersion(buf); - if (!server_version.equals(context.getServerVersion())) + 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); } - - byte[] cookie = TlsUtils.readOpaque8(buf); - - // TODO RFC 4347 has the cookie length restricted to 32, but not in RFC 6347 - - TlsProtocol.assertEmpty(buf); - - return cookie; } protected static byte[] patchClientHelloWithCookie(byte[] clientHelloBody, byte[] cookie) throws IOException { - int sessionIDPos = 34; int sessionIDLength = TlsUtils.readUint8(clientHelloBody, sessionIDPos); @@ -607,7 +779,8 @@ public class DTLSClientProtocol byte[] patched = new byte[clientHelloBody.length + cookie.length]; System.arraycopy(clientHelloBody, 0, patched, 0, cookieLengthPos); - TlsUtils.writeUint8((short)cookie.length, patched, 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); @@ -619,15 +792,22 @@ public class DTLSClientProtocol { 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java index 2789b22..e27580c 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java @@ -4,18 +4,17 @@ 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.bouncycastle.util.Arrays; public abstract class DTLSProtocol { - protected final SecureRandom secureRandom; protected DTLSProtocol(SecureRandom secureRandom) { - if (secureRandom == null) { throw new IllegalArgumentException("'secureRandom' cannot be null"); @@ -27,7 +26,6 @@ public abstract class DTLSProtocol 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); @@ -40,10 +38,20 @@ public abstract class DTLSProtocol } } + 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(); @@ -52,7 +60,6 @@ public abstract class DTLSProtocol protected static byte[] generateSupplementalData(Vector supplementalData) throws IOException { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); TlsProtocol.writeSupplementalData(buf, supplementalData); return buf.toByteArray(); @@ -61,7 +68,6 @@ public abstract class DTLSProtocol protected static void validateSelectedCipherSuite(int selectedCipherSuite, short alertDescription) throws IOException { - switch (selectedCipherSuite) { case CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5: diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java index 3fde01a..cba13d5 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java @@ -5,7 +5,6 @@ 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; @@ -21,6 +20,7 @@ class DTLSRecordLayer 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; @@ -40,6 +40,13 @@ class DTLSRecordLayer this.pendingEpoch = null; this.readEpoch = currentEpoch; this.writeEpoch = currentEpoch; + + setPlaintextLimit(MAX_FRAGMENT_LENGTH); + } + + void setPlaintextLimit(int plaintextLimit) + { + this.plaintextLimit = plaintextLimit; } ProtocolVersion getDiscoveredPeerVersion() @@ -47,6 +54,13 @@ class DTLSRecordLayer return discoveredPeerVersion; } + ProtocolVersion resetDiscoveredPeerVersion() + { + ProtocolVersion result = discoveredPeerVersion; + discoveredPeerVersion = null; + return result; + } + void initPendingEpoch(TlsCipher pendingCipher) { if (pendingEpoch != null) @@ -99,26 +113,24 @@ class DTLSRecordLayer public int getReceiveLimit() throws IOException { - return Math.min(MAX_FRAGMENT_LENGTH, + return Math.min(this.plaintextLimit, readEpoch.getCipher().getPlaintextLimit(transport.getReceiveLimit() - RECORD_HEADER_LENGTH)); } public int getSendLimit() throws IOException { - return Math.min(MAX_FRAGMENT_LENGTH, + 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 (; ; ) + for (;;) { - int receiveLimit = Math.min(len, getReceiveLimit()) + RECORD_HEADER_LENGTH; if (record == null || record.length < receiveLimit) { @@ -157,6 +169,7 @@ class DTLSRecordLayer case ContentType.application_data: case ContentType.change_cipher_spec: case ContentType.handshake: + case ContentType.heartbeat: break; default: // TODO Exception? @@ -199,6 +212,11 @@ class DTLSRecordLayer recordEpoch.getReplayWindow().reportAuthenticated(seq); + if (plaintext.length > this.plaintextLimit) + { + continue; + } + if (discoveredPeerVersion == null) { discoveredPeerVersion = version; @@ -208,7 +226,6 @@ class DTLSRecordLayer { case ContentType.alert: { - if (plaintext.length == 2) { short alertLevel = plaintext[0]; @@ -249,14 +266,18 @@ class DTLSRecordLayer { // Implicitly receive change_cipher_spec and change to pending cipher state - if (plaintext.length != 1 || plaintext[0] != 1) + for (int i = 0; i < plaintext.length; ++i) { - continue; - } + short message = TlsUtils.readUint8(plaintext, i); + if (message != ChangeCipherSpec.change_cipher_spec) + { + continue; + } - if (pendingEpoch != null) - { - readEpoch = pendingEpoch; + if (pendingEpoch != null) + { + readEpoch = pendingEpoch; + } } continue; @@ -273,6 +294,12 @@ class DTLSRecordLayer // TODO Consider support for HelloRequest continue; } + break; + } + case ContentType.heartbeat: + { + // TODO[RFC 6520] + continue; } } @@ -300,18 +327,15 @@ class DTLSRecordLayer 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) { @@ -331,7 +355,7 @@ class DTLSRecordLayer // 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}; + byte[] data = new byte[]{ 1 }; sendRecord(ContentType.change_cipher_spec, data, 0, data.length); writeEpoch = nextEpoch; @@ -410,7 +434,6 @@ class DTLSRecordLayer private void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause) throws IOException { - peer.notifyAlertRaised(alertLevel, alertDescription, message, cause); byte[] error = new byte[2]; @@ -434,8 +457,7 @@ class DTLSRecordLayer } int received = Math.min(recordQueue.size(), RECORD_HEADER_LENGTH + length); - recordQueue.read(buf, off, received, 0); - recordQueue.removeData(received); + recordQueue.removeData(buf, off, received, 0); return received; } @@ -457,6 +479,10 @@ class DTLSRecordLayer 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, @@ -473,10 +499,7 @@ class DTLSRecordLayer byte[] ciphertext = writeEpoch.getCipher().encodePlaintext( getMacSequenceNumber(recordEpoch, recordSequenceNumber), contentType, buf, off, len); - if (ciphertext.length > MAX_FRAGMENT_LENGTH) - { - throw new TlsFatalAlert(AlertDescription.internal_error); - } + // TODO Check the ciphertext length? byte[] record = new byte[ciphertext.length + RECORD_HEADER_LENGTH]; TlsUtils.writeUint8(contentType, record, 0); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java index 3819251..84ccfcb 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java @@ -10,12 +10,11 @@ import org.bouncycastle.util.Integers; class DTLSReliableHandshake { - private final static int MAX_RECEIVE_AHEAD = 10; private final DTLSRecordLayer recordLayer; - private TlsHandshakeHash hash = new DeferredHash(); + private TlsHandshakeHash handshakeHash; private Hashtable currentInboundFlight = new Hashtable(); private Hashtable previousInboundFlight = null; @@ -27,25 +26,31 @@ class DTLSReliableHandshake DTLSReliableHandshake(TlsContext context, DTLSRecordLayer transport) { this.recordLayer = transport; - this.hash.init(context); + this.handshakeHash = new DeferredHash(); + this.handshakeHash.init(context); } void notifyHelloComplete() { - this.hash = this.hash.commit(); + this.handshakeHash = handshakeHash.notifyPRFDetermined(); + } + + TlsHandshakeHash getHandshakeHash() + { + return handshakeHash; } - byte[] getCurrentHash() + TlsHandshakeHash prepareToFinish() { - TlsHandshakeHash copyOfHash = hash.fork(); - byte[] result = new byte[copyOfHash.getDigestSize()]; - copyOfHash.doFinal(result, 0); + TlsHandshakeHash result = handshakeHash; + this.handshakeHash = handshakeHash.stopTracking(); return result; } void sendMessage(short msg_type, byte[] body) throws IOException { + TlsUtils.checkUint24(body.length); if (!sending) { @@ -62,10 +67,21 @@ class DTLSReliableHandshake updateHandshakeMessagesDigest(message); } - Message receiveMessage() + 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; @@ -93,7 +109,6 @@ class DTLSReliableHandshake for (; ; ) { - int receiveLimit = recordLayer.getReceiveLimit(); if (buf == null || buf.length < receiveLimit) { @@ -280,7 +295,7 @@ class DTLSReliableHandshake void resetHandshakeMessagesDigest() { - hash.reset(); + handshakeHash.reset(); } /** @@ -328,8 +343,8 @@ class DTLSReliableHandshake TlsUtils.writeUint16(message.getSeq(), buf, 4); TlsUtils.writeUint24(0, buf, 6); TlsUtils.writeUint24(body.length, buf, 9); - hash.update(buf, 0, buf.length); - hash.update(body, 0, body.length); + handshakeHash.update(buf, 0, buf.length); + handshakeHash.update(body, 0, body.length); } return message; } @@ -337,7 +352,6 @@ class DTLSReliableHandshake private void writeMessage(Message message) throws IOException { - int sendLimit = recordLayer.getSendLimit(); int fragmentLimit = sendLimit - 12; @@ -364,18 +378,15 @@ class DTLSReliableHandshake private void writeHandshakeFragment(Message message, int fragment_offset, int fragment_length) throws IOException { - - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeUint8(message.getType(), buf); - TlsUtils.writeUint24(message.getBody().length, buf); - TlsUtils.writeUint16(message.getSeq(), buf); - TlsUtils.writeUint24(fragment_offset, buf); - TlsUtils.writeUint24(fragment_length, buf); - buf.write(message.getBody(), fragment_offset, fragment_length); - - byte[] fragment = buf.toByteArray(); - - recordLayer.send(fragment, 0, fragment.length); + 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) @@ -402,7 +413,6 @@ class DTLSReliableHandshake static class Message { - private final int message_seq; private final short msg_type; private final byte[] body; @@ -429,4 +439,18 @@ class DTLSReliableHandshake 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java index 3a100d1..fbb3336 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java @@ -15,7 +15,6 @@ import org.bouncycastle.util.Arrays; public class DTLSServerProtocol extends DTLSProtocol { - protected boolean verifyRequests = true; public DTLSServerProtocol(SecureRandom secureRandom) @@ -36,7 +35,6 @@ public class DTLSServerProtocol public DTLSTransport accept(TlsServer server, DatagramTransport transport) throws IOException { - if (server == null) { throw new IllegalArgumentException("'server' cannot be null"); @@ -80,10 +78,9 @@ public class DTLSServerProtocol } } - public DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer) + protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer) throws IOException { - SecurityParameters securityParameters = state.serverContext.getSecurityParameters(); DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.serverContext, recordLayer); @@ -105,23 +102,31 @@ public class DTLSServerProtocol throw new TlsFatalAlert(AlertDescription.unexpected_message); } - byte[] serverHelloBody = generateServerHello(state); - handshake.sendMessage(HandshakeType.server_hello, serverHelloBody); - - // TODO This block could really be done before actually sending the hello { - securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.selectedCipherSuite); + 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.notifyHelloComplete(); + + handshake.sendMessage(HandshakeType.server_hello, serverHelloBody); } + handshake.notifyHelloComplete(); + Vector serverSupplementalData = state.server.getServerSupplementalData(); if (serverSupplementalData != null) { @@ -133,6 +138,9 @@ public class DTLSServerProtocol state.keyExchange.init(state.serverContext); state.serverCredentials = state.server.getCredentials(); + + Certificate serverCertificate = null; + if (state.serverCredentials == null) { state.keyExchange.skipServerCredentials(); @@ -141,10 +149,27 @@ public class DTLSServerProtocol { state.keyExchange.processServerCredentials(state.serverCredentials); - byte[] certificateBody = generateCertificate(state.serverCredentials.getCertificate()); + 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) { @@ -160,11 +185,16 @@ public class DTLSServerProtocol 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) @@ -190,9 +220,7 @@ public class DTLSServerProtocol } else { - ProtocolVersion equivalentTLSVersion = state.serverContext.getServerVersion().getEquivalentTLSVersion(); - - if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(equivalentTLSVersion)) + if (TlsUtils.isTLSv12(state.serverContext)) { /* * RFC 5246 If no suitable certificate is available, the client MUST send a @@ -216,8 +244,11 @@ public class DTLSServerProtocol 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 @@ -225,33 +256,14 @@ public class DTLSServerProtocol */ if (expectCertificateVerifyMessage(state)) { - byte[] certificateVerifyHash = handshake.getCurrentHash(); - clientMessage = handshake.receiveMessage(); - - if (clientMessage.getType() == HandshakeType.certificate_verify) - { - processCertificateVerify(state, clientMessage.getBody(), certificateVerifyHash); - } - else - { - throw new TlsFatalAlert(AlertDescription.unexpected_message); - } + byte[] certificateVerifyBody = handshake.receiveMessageBody(HandshakeType.certificate_verify); + processCertificateVerify(state, certificateVerifyBody, prepareFinishHash); } // NOTE: Calculated exclusive of the actual Finished message from the client - byte[] clientFinishedHash = handshake.getCurrentHash(); - clientMessage = handshake.receiveMessage(); - - if (clientMessage.getType() == HandshakeType.finished) - { - byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, "client finished", - clientFinishedHash); - processFinished(clientMessage.getBody(), expectedClientVerifyData); - } - else - { - throw new TlsFatalAlert(AlertDescription.unexpected_message); - } + 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) { @@ -261,8 +273,8 @@ public class DTLSServerProtocol } // NOTE: Calculated exclusive of the Finished message itself - byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, "server finished", - handshake.getCurrentHash()); + byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.server_finished, + TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null)); handshake.sendMessage(HandshakeType.finished, serverVerifyData); handshake.finish(); @@ -275,16 +287,22 @@ public class DTLSServerProtocol protected byte[] generateCertificateRequest(ServerHandshakeState state, CertificateRequest certificateRequest) throws IOException { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); certificateRequest.encode(buf); return buf.toByteArray(); } - protected byte[] generateNewSessionTicket(ServerHandshakeState state, NewSessionTicket newSessionTicket) + 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(); @@ -293,6 +311,7 @@ public class DTLSServerProtocol protected byte[] generateServerHello(ServerHandshakeState state) throws IOException { + SecurityParameters securityParameters = state.serverContext.getSecurityParameters(); ByteArrayOutputStream buf = new ByteArrayOutputStream(); @@ -310,7 +329,7 @@ public class DTLSServerProtocol TlsUtils.writeVersion(state.serverContext.getServerVersion(), buf); - buf.write(state.serverContext.getSecurityParameters().serverRandom); + buf.write(securityParameters.getServerRandom()); /* * The server may return an empty session_id to indicate that the session will not be cached @@ -319,7 +338,7 @@ public class DTLSServerProtocol TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf); state.selectedCipherSuite = state.server.getSelectedCipherSuite(); - if (!TlsProtocol.arrayContains(state.offeredCipherSuites, state.selectedCipherSuite) + if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite) || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) { @@ -329,7 +348,7 @@ public class DTLSServerProtocol validateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.internal_error); state.selectedCompressionMethod = state.server.getSelectedCompressionMethod(); - if (!TlsProtocol.arrayContains(state.offeredCompressionMethods, state.selectedCompressionMethod)) + if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod)) { throw new TlsFatalAlert(AlertDescription.internal_error); } @@ -344,9 +363,8 @@ public class DTLSServerProtocol */ if (state.secure_renegotiation) { - - boolean noRenegExt = state.serverExtensions == null - || !state.serverExtensions.containsKey(TlsProtocol.EXT_RenegotiationInfo); + byte[] renegExtData = TlsUtils.getExtensionData(state.serverExtensions, TlsProtocol.EXT_RenegotiationInfo); + boolean noRenegExt = (null == renegExtData); if (noRenegExt) { @@ -357,15 +375,12 @@ public class DTLSServerProtocol * because the client is signaling its willingness to receive the extension via the * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. */ - if (state.serverExtensions == null) - { - state.serverExtensions = new Hashtable(); - } /* * 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)); } @@ -373,7 +388,17 @@ public class DTLSServerProtocol if (state.serverExtensions != null) { - state.expectSessionTicket = state.serverExtensions.containsKey(TlsProtocol.EXT_SessionTicket); + 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); } @@ -383,7 +408,6 @@ public class DTLSServerProtocol protected void notifyClientCertificate(ServerHandshakeState state, Certificate clientCertificate) throws IOException { - if (state.certificateRequest == null) { throw new IllegalStateException(); @@ -429,7 +453,6 @@ public class DTLSServerProtocol protected void processClientCertificate(ServerHandshakeState state, byte[] body) throws IOException { - ByteArrayInputStream buf = new ByteArrayInputStream(body); Certificate clientCertificate = Certificate.parse(buf); @@ -439,27 +462,29 @@ public class DTLSServerProtocol notifyClientCertificate(state, clientCertificate); } - protected void processCertificateVerify(ServerHandshakeState state, byte[] body, byte[] certificateVerifyHash) + protected void processCertificateVerify(ServerHandshakeState state, byte[] body, TlsHandshakeHash prepareFinishHash) throws IOException { - ByteArrayInputStream buf = new ByteArrayInputStream(body); - byte[] clientCertificateSignature = TlsUtils.readOpaque16(buf); + DigitallySigned clientCertificateVerify = DigitallySigned.parse(state.serverContext, buf); TlsProtocol.assertEmpty(buf); // Verify the CertificateVerify message contains a correct signature. try { - TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType); - tlsSigner.init(state.serverContext); + // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned + byte[] certificateVerifyHash = TlsProtocol.getCurrentPRFHash(state.serverContext, prepareFinishHash, null); org.bouncycastle.asn1.x509.Certificate x509Cert = state.clientCertificate.getCertificateAt(0); SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo); - tlsSigner.verifyRawSignature(clientCertificateSignature, publicKey, certificateVerifyHash); + TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType); + tlsSigner.init(state.serverContext); + tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(), + clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash); } catch (Exception e) { @@ -470,7 +495,6 @@ public class DTLSServerProtocol 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 @@ -545,7 +569,7 @@ public class DTLSServerProtocol * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag * to TRUE. */ - if (TlsProtocol.arrayContains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) + if (Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { state.secure_renegotiation = true; } @@ -554,23 +578,19 @@ public class DTLSServerProtocol * The server MUST check if the "renegotiation_info" extension is included in the * ClientHello. */ - if (state.clientExtensions != null) + byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo); + if (renegExtData != null) { - byte[] renegExtValue = (byte[])state.clientExtensions.get(TlsProtocol.EXT_RenegotiationInfo); - if (renegExtValue != 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 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(renegExtValue, - TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) - { - throw new TlsFatalAlert(AlertDescription.handshake_failure); - } + if (!Arrays.constantTimeAreEqual(renegExtData, TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } @@ -586,20 +606,16 @@ public class DTLSServerProtocol protected void processClientKeyExchange(ServerHandshakeState state, byte[] body) throws IOException { - ByteArrayInputStream buf = new ByteArrayInputStream(body); state.keyExchange.processClientKeyExchange(buf); TlsProtocol.assertEmpty(buf); - - TlsProtocol.establishMasterSecret(state.serverContext, state.keyExchange); } protected void processClientSupplementalData(ServerHandshakeState state, byte[] body) throws IOException { - ByteArrayInputStream buf = new ByteArrayInputStream(body); Vector clientSupplementalData = TlsProtocol.readSupplementalDataMessage(buf); state.server.processClientSupplementalData(clientSupplementalData); @@ -620,6 +636,8 @@ public class DTLSServerProtocol 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; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java index 98efc4f..78afb41 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java @@ -11,9 +11,8 @@ import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.util.BigIntegers; public class DefaultTlsAgreementCredentials - implements TlsAgreementCredentials + extends AbstractTlsAgreementCredentials { - protected Certificate certificate; protected AsymmetricKeyParameter privateKey; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java index 82b37d9..7f70c64 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java @@ -4,6 +4,7 @@ import java.io.IOException; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.StreamCipher; import org.bouncycastle.crypto.digests.MD5Digest; import org.bouncycastle.crypto.digests.SHA1Digest; @@ -15,24 +16,37 @@ import org.bouncycastle.crypto.engines.CamelliaEngine; import org.bouncycastle.crypto.engines.DESedeEngine; import org.bouncycastle.crypto.engines.RC4Engine; import org.bouncycastle.crypto.engines.SEEDEngine; +import org.bouncycastle.crypto.engines.Salsa20Engine; +import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.CCMBlockCipher; import org.bouncycastle.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); @@ -45,10 +59,14 @@ public class DefaultTlsCipherFactory 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: @@ -63,6 +81,13 @@ public class DefaultTlsCipherFactory 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 { @@ -70,8 +95,7 @@ public class DefaultTlsCipherFactory createAEADBlockCipher_AES_GCM(), cipherKeySize, macSize); } - protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize, - int macAlgorithm) + protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm) throws IOException { return new TlsBlockCipher(context, createCamelliaBlockCipher(), @@ -79,6 +103,13 @@ public class DefaultTlsCipherFactory 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 { @@ -86,19 +117,22 @@ public class DefaultTlsCipherFactory createHMACDigest(macAlgorithm)); } - protected TlsStreamCipher createRC4Cipher(TlsContext context, int cipherKeySize, - int macAlgorithm) + protected TlsStreamCipher createRC4Cipher(TlsContext context, int cipherKeySize, int macAlgorithm) throws IOException { return new TlsStreamCipher(context, createRC4StreamCipher(), createRC4StreamCipher(), createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize); } - protected TlsBlockCipher createDESedeCipher(TlsContext context, int macAlgorithm) + protected TlsStreamCipher createSalsa20Cipher(TlsContext context, int rounds, int cipherKeySize, int macAlgorithm) throws IOException { - return new TlsBlockCipher(context, createDESedeBlockCipher(), createDESedeBlockCipher(), - createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 24); + /* + * 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) @@ -108,14 +142,14 @@ public class DefaultTlsCipherFactory createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 16); } - protected StreamCipher createRC4StreamCipher() + protected BlockCipher createAESBlockCipher() { - return new RC4Engine(); + return new CBCBlockCipher(new AESFastEngine()); } - protected BlockCipher createAESBlockCipher() + protected AEADBlockCipher createAEADBlockCipher_AES_CCM() { - return new CBCBlockCipher(new AESFastEngine()); + return new CCMBlockCipher(new AESFastEngine()); } protected AEADBlockCipher createAEADBlockCipher_AES_GCM() @@ -134,13 +168,22 @@ public class DefaultTlsCipherFactory 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 + protected Digest createHMACDigest(int macAlgorithm) throws IOException { switch (macAlgorithm) { @@ -160,4 +203,16 @@ public class DefaultTlsCipherFactory 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java index 4f9fe27..63db45f 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java @@ -1,15 +1,10 @@ package org.bouncycastle.crypto.tls; import java.io.IOException; -import java.util.Hashtable; public abstract class DefaultTlsClient extends AbstractTlsClient { - - protected int[] namedCurves; - protected short[] clientECPointFormats, serverECPointFormats; - public DefaultTlsClient() { super(); @@ -22,75 +17,15 @@ public abstract class DefaultTlsClient public 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 Hashtable getClientExtensions() - throws IOException - { - - Hashtable clientExtensions = super.getClientExtensions(); - - 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.sect233r1, NamedCurve.secp224r1, - NamedCurve.sect193r1, NamedCurve.secp192r1, NamedCurve.arbitrary_explicit_char2_curves, - NamedCurve.arbitrary_explicit_prime_curves}; - this.clientECPointFormats = new short[]{ECPointFormat.ansiX962_compressed_char2, - ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed}; - - if (clientExtensions == null) - { - clientExtensions = new Hashtable(); - } - - TlsECCUtils.addSupportedEllipticCurvesExtension(clientExtensions, namedCurves); - TlsECCUtils.addSupportedPointFormatsExtension(clientExtensions, clientECPointFormats); - } - - return clientExtensions; - } - - public void processServerExtensions(Hashtable serverExtensions) - throws IOException - { - - super.processServerExtensions(serverExtensions); - - if (serverExtensions != null) - { - 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); - } - } + 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: @@ -132,12 +67,20 @@ public abstract class DefaultTlsClient 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); @@ -170,8 +113,12 @@ public abstract class DefaultTlsClient 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: @@ -181,24 +128,36 @@ public abstract class DefaultTlsClient 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(); @@ -215,7 +174,6 @@ public abstract class DefaultTlsClient public TlsCipher getCipher() throws IOException { - switch (selectedCipherSuite) { case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: @@ -251,6 +209,14 @@ public abstract class DefaultTlsClient 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: @@ -286,6 +252,14 @@ public abstract class DefaultTlsClient 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: @@ -311,6 +285,18 @@ public abstract class DefaultTlsClient 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); @@ -334,6 +320,18 @@ public abstract class DefaultTlsClient 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: diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java index a338c38..dcdb493 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java @@ -10,14 +10,14 @@ import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.params.RSAKeyParameters; public class DefaultTlsEncryptionCredentials - implements TlsEncryptionCredentials + extends AbstractTlsEncryptionCredentials { protected TlsContext context; protected Certificate certificate; protected AsymmetricKeyParameter privateKey; public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate, - AsymmetricKeyParameter privateKey) + AsymmetricKeyParameter privateKey) { if (certificate == null) { diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java index 246b87e..31b6a74 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java @@ -48,15 +48,18 @@ public abstract class DefaultTlsServer 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: @@ -71,9 +74,13 @@ public abstract class DefaultTlsServer 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: @@ -85,6 +92,8 @@ public abstract class DefaultTlsServer 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: @@ -98,7 +107,6 @@ public abstract class DefaultTlsServer public TlsKeyExchange getKeyExchange() throws IOException { - switch (selectedCipherSuite) { case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: @@ -140,12 +148,20 @@ public abstract class DefaultTlsServer 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); @@ -178,8 +194,12 @@ public abstract class DefaultTlsServer 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: @@ -189,24 +209,36 @@ public abstract class DefaultTlsServer 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(); @@ -221,7 +253,6 @@ public abstract class DefaultTlsServer public TlsCipher getCipher() throws IOException { - switch (selectedCipherSuite) { case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA: @@ -257,6 +288,14 @@ public abstract class DefaultTlsServer 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: @@ -292,6 +331,14 @@ public abstract class DefaultTlsServer 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: @@ -317,6 +364,18 @@ public abstract class DefaultTlsServer 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); @@ -340,6 +399,18 @@ public abstract class DefaultTlsServer 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: diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java index b775250..15bf4a4 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java @@ -9,17 +9,23 @@ import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.RSAKeyParameters; public class DefaultTlsSignerCredentials - implements TlsSignerCredentials + 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"); @@ -36,6 +42,10 @@ public class DefaultTlsSignerCredentials { 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) { @@ -59,6 +69,7 @@ public class DefaultTlsSignerCredentials this.context = context; this.certificate = certificate; this.privateKey = privateKey; + this.signatureAndHashAlgorithm = signatureAndHashAlgorithm; } public Certificate getCertificate() @@ -66,16 +77,28 @@ public class DefaultTlsSignerCredentials return certificate; } - public byte[] generateCertificateSignature(byte[] md5andsha1) + public byte[] generateCertificateSignature(byte[] hash) throws IOException { try { - return signer.generateRawSignature(privateKey, md5andsha1); + 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java index e8c76e6..274e69a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java @@ -1,8 +1,10 @@ package org.bouncycastle.crypto.tls; -import java.io.ByteArrayOutputStream; +import java.util.Enumeration; +import java.util.Hashtable; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.util.Shorts; /** * Buffers input until the hash algorithm is determined. @@ -10,23 +12,27 @@ import org.bouncycastle.crypto.Digest; class DeferredHash implements TlsHandshakeHash { + protected static final int BUFFERING_HASH_LIMIT = 4; protected TlsContext context; - private ByteArrayOutputStream buf = new ByteArrayOutputStream(); - private int prfAlgorithm = -1; - private Digest hash = null; + private DigestInputBuffer buf; + private Hashtable hashes; + private Short prfHashAlgorithm; DeferredHash() { - this.buf = new ByteArrayOutputStream(); - this.hash = null; + this.buf = new DigestInputBuffer(); + this.hashes = new Hashtable(); + this.prfHashAlgorithm = null; } - private DeferredHash(Digest hash) + private DeferredHash(Short prfHashAlgorithm, Digest prfHash) { this.buf = null; - this.hash = hash; + this.hashes = new Hashtable(); + this.prfHashAlgorithm = prfHashAlgorithm; + hashes.put(prfHashAlgorithm, prfHash); } public void init(TlsContext context) @@ -34,95 +40,168 @@ class DeferredHash this.context = context; } - public TlsHandshakeHash commit() + 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(); + } - Digest prfHash = TlsUtils.createPRFHash(prfAlgorithm); + this.prfHashAlgorithm = Shorts.valueOf(TlsUtils.getHashAlgorithmForPRFAlgorithm(prfAlgorithm)); - byte[] data = buf.toByteArray(); - prfHash.update(data, 0, data.length); + checkTrackingHash(prfHashAlgorithm); + + return this; + } - if (prfHash instanceof TlsHandshakeHash) + public void trackHashAlgorithm(short hashAlgorithm) + { + if (buf == null) { - TlsHandshakeHash tlsPRFHash = (TlsHandshakeHash)prfHash; - tlsPRFHash.init(context); - return tlsPRFHash.commit(); + throw new IllegalStateException("Too late to track more hash algorithms"); } - this.prfAlgorithm = prfAlgorithm; - this.hash = prfHash; - this.buf = null; + checkTrackingHash(Shorts.valueOf(hashAlgorithm)); + } - return this; + public void sealHashAlgorithms() + { + checkStopBuffering(); } - public TlsHandshakeHash fork() + public TlsHandshakeHash stopTracking() { - checkHash(); - return new DeferredHash(TlsUtils.clonePRFHash(prfAlgorithm, hash)); + 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() { - checkHash(); - return hash.getAlgorithmName(); + throw new IllegalStateException("Use fork() to get a definite Digest"); } public int getDigestSize() { - checkHash(); - return hash.getDigestSize(); + throw new IllegalStateException("Use fork() to get a definite Digest"); } public void update(byte input) { - if (hash == null) + if (buf != null) { buf.write(input); + return; } - else + + 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 (hash == null) + if (buf != null) { buf.write(input, inOff, len); + return; } - else + + Enumeration e = hashes.elements(); + while (e.hasMoreElements()) { + Digest hash = (Digest)e.nextElement(); hash.update(input, inOff, len); } } public int doFinal(byte[] output, int outOff) { - checkHash(); - return hash.doFinal(output, outOff); + throw new IllegalStateException("Use fork() to get a definite Digest"); } public void reset() { - if (hash == null) + if (buf != null) { buf.reset(); + return; } - else + + Enumeration e = hashes.elements(); + while (e.hasMoreElements()) { + Digest hash = (Digest)e.nextElement(); hash.reset(); } } - protected void checkHash() + 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 (hash == null) + if (!hashes.containsKey(hashAlgorithm)) { - throw new IllegalStateException("No hash algorithm has been set"); + Digest hash = TlsUtils.createHash(hashAlgorithm.shortValue()); + hashes.put(hashAlgorithm, hash); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java new file mode 100644 index 0000000..7cfa890 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java @@ -0,0 +1,13 @@ +package org.bouncycastle.crypto.tls; + +import java.io.ByteArrayOutputStream; + +import org.bouncycastle.crypto.Digest; + +class DigestInputBuffer extends ByteArrayOutputStream +{ + void updateDigest(Digest d) + { + d.update(this.buf, 0, count); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java new file mode 100644 index 0000000..c366ca9 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java @@ -0,0 +1,72 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java index f991e4a..6f3fe01 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java @@ -25,6 +25,12 @@ public class EncryptionAlgorithm 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; @@ -36,8 +42,16 @@ public class EncryptionAlgorithm public static final int SEED_CBC = 14; /* - * RFC 5289 + * RFC 6655 */ - public static final int AES_128_GCM = 10; - public static final int AES_256_GCM = 11; + 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java index 0be6465..8312e93 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java @@ -3,7 +3,7 @@ package org.bouncycastle.crypto.tls; public class ExtensionType { /* - * RFC 6066 1.1. + * RFC 2546 2.3. */ public static final int server_name = 0; public static final int max_fragment_length = 1; @@ -44,6 +44,11 @@ public class ExtensionType 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java index 53b4520..c81660a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java @@ -17,6 +17,12 @@ public class HandshakeType 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; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java index ac0a4c6..dc7482b 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java @@ -5,7 +5,6 @@ package org.bouncycastle.crypto.tls; */ public class HashAlgorithm { - public static final short none = 0; public static final short md5 = 1; public static final short sha1 = 2; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java new file mode 100644 index 0000000..f9f3670 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java @@ -0,0 +1,56 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java new file mode 100644 index 0000000..320a128 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java @@ -0,0 +1,108 @@ +package org.bouncycastle.crypto.tls; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.bouncycastle.util.Arrays; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java new file mode 100644 index 0000000..f1a3b43 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java @@ -0,0 +1,15 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java new file mode 100644 index 0000000..4024faa --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java @@ -0,0 +1,15 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java index c049bb7..72a944f 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java @@ -44,4 +44,9 @@ public class KeyExchangeAlgorithm 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java index 40ef15c..92adc8c 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java @@ -8,7 +8,6 @@ package org.bouncycastle.crypto.tls; */ public class MACAlgorithm { - public static final int _null = 0; public static final int md5 = 1; public static final int sha = 2; @@ -21,4 +20,9 @@ public class MACAlgorithm 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java new file mode 100644 index 0000000..413695c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java @@ -0,0 +1,17 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java new file mode 100644 index 0000000..9b0cd1b --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java @@ -0,0 +1,9 @@ +package org.bouncycastle.crypto.tls; + +public class NameType +{ + /* + * RFC 3546 3.1. + */ + public static final short host_name = 0; +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java index 690115c..a965d13 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java @@ -36,6 +36,13 @@ public class NamedCurve 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) diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java index f3d1022..8f87a65 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java @@ -6,7 +6,6 @@ import java.io.OutputStream; public class NewSessionTicket { - protected long ticketLifetimeHint; protected byte[] ticket; @@ -26,6 +25,12 @@ public class NewSessionTicket 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 { @@ -33,6 +38,13 @@ public class NewSessionTicket 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 { diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java new file mode 100644 index 0000000..db8168f --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java @@ -0,0 +1,131 @@ +package org.bouncycastle.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.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ocsp.ResponderID; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java index 29750cb..92475b2 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java @@ -21,41 +21,89 @@ public abstract class PSKTlsClient public int[] getCipherSuites() { - return new int[]{CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA, - CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA, - CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_PSK_WITH_RC4_128_SHA,}; + 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 + 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); - 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_256_CBC_SHA: - case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA: - case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA: - return createPSKKeyExchange(KeyExchangeAlgorithm.DHE_PSK); - default: /* * Note: internal error here; the TlsProtocol implementation verifies that the @@ -66,37 +114,114 @@ public abstract class PSKTlsClient } } - public TlsCipher getCipher() - throws IOException + 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: - case CipherSuite.TLS_DHE_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: - case CipherSuite.TLS_DHE_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: - case CipherSuite.TLS_DHE_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: - case CipherSuite.TLS_DHE_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: - case CipherSuite.TLS_DHE_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 @@ -109,6 +234,7 @@ public abstract class PSKTlsClient protected TlsKeyExchange createPSKKeyExchange(int keyExchange) { - return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity); + return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity, null, namedCurves, + clientECPointFormats, serverECPointFormats); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java index c001e58..b32bd9d 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java @@ -4,7 +4,6 @@ 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"); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java index 3a31c20..cc6640b 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java @@ -12,10 +12,7 @@ import org.bouncycastle.crypto.Digest; */ class RecordStream { - - private static int PLAINTEXT_LIMIT = (1 << 14); - private static int COMPRESSED_LIMIT = PLAINTEXT_LIMIT + 1024; - private static int CIPHERTEXT_LIMIT = COMPRESSED_LIMIT + 1024; + private static int DEFAULT_PLAINTEXT_LIMIT = (1 << 14); private TlsProtocol handler; private InputStream input; @@ -26,11 +23,13 @@ class RecordStream private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); private TlsContext context = null; - private TlsHandshakeHash hash = 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; @@ -40,13 +39,27 @@ class RecordStream 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.hash = new DeferredHash(); - this.hash.init(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() @@ -76,11 +89,6 @@ class RecordStream this.restrictReadVersion = enabled; } - void notifyHelloComplete() - { - this.hash = this.hash.commit(); - } - void setPendingConnectionState(TlsCompression tlsCompression, TlsCipher tlsCipher) { this.pendingCompression = tlsCompression; @@ -123,13 +131,17 @@ class RecordStream pendingCipher = null; } - public void readRecord() + public boolean readRecord() throws IOException { + byte[] recordHeader = TlsUtils.readAllOrNothing(5, input); + if (recordHeader == null) + { + return false; + } - short type = TlsUtils.readUint8(input); + short type = TlsUtils.readUint8(recordHeader, 0); - // TODO In earlier RFCs, it was "SHOULD ignore"; should this be version-dependent? /* * RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an * unexpected_message alert. @@ -138,7 +150,7 @@ class RecordStream if (!restrictReadVersion) { - int version = TlsUtils.readVersionRaw(input); + int version = TlsUtils.readVersionRaw(recordHeader, 1); if ((version & 0xffffff00) != 0x0300) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); @@ -146,7 +158,7 @@ class RecordStream } else { - ProtocolVersion version = TlsUtils.readVersion(input); + ProtocolVersion version = TlsUtils.readVersion(recordHeader, 1); if (readVersion == null) { readVersion = version; @@ -157,21 +169,21 @@ class RecordStream } } - int length = TlsUtils.readUint16(input); + 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, CIPHERTEXT_LIMIT, AlertDescription.record_overflow); + 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, COMPRESSED_LIMIT, AlertDescription.record_overflow); + checkLength(decoded.length, compressedLimit, AlertDescription.record_overflow); /* * TODO RFC5264 6.2.2. Implementation note: Decompression functions are responsible for @@ -190,7 +202,16 @@ class RecordStream * would decompress to a length in excess of 2^14 bytes, it should report a fatal * decompression failure error. */ - checkLength(decoded.length, PLAINTEXT_LIMIT, AlertDescription.decompression_failure); + 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; } @@ -198,7 +219,6 @@ class RecordStream 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. @@ -208,7 +228,7 @@ class RecordStream /* * RFC 5264 6.2.1 The length should not exceed 2^14. */ - checkLength(plaintextLength, PLAINTEXT_LIMIT, AlertDescription.internal_error); + checkLength(plaintextLength, plaintextLimit, AlertDescription.internal_error); /* * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, @@ -249,7 +269,7 @@ class RecordStream /* * RFC 5264 6.2.3. The length may not exceed 2^14 + 2048. */ - checkLength(ciphertext.length, CIPHERTEXT_LIMIT, AlertDescription.internal_error); + checkLength(ciphertext.length, ciphertextLimit, AlertDescription.internal_error); byte[] record = new byte[ciphertext.length + 5]; TlsUtils.writeUint8(type, record, 0); @@ -260,52 +280,44 @@ class RecordStream output.flush(); } - void updateHandshakeData(byte[] message, int offset, int len) + void notifyHelloComplete() { - hash.update(message, offset, len); + this.handshakeHash = handshakeHash.notifyPRFDetermined(); } - /** - * 'sender' only relevant to SSLv3 - */ - byte[] getCurrentHash(byte[] sender) + TlsHandshakeHash getHandshakeHash() { - TlsHandshakeHash d = hash.fork(); + return handshakeHash; + } - if (context.getServerVersion().isSSL()) - { - if (sender != null) - { - d.update(sender, 0, sender.length); - } - } + TlsHandshakeHash prepareToFinish() + { + TlsHandshakeHash result = handshakeHash; + this.handshakeHash = handshakeHash.stopTracking(); + return result; + } - return doFinal(d); + void updateHandshakeData(byte[] message, int offset, int len) + { + handshakeHash.update(message, offset, len); } - protected void close() - throws IOException + protected void safeClose() { - IOException e = null; try { input.close(); } - catch (IOException ex) + catch (IOException e) { - e = ex; } + try { output.close(); } - catch (IOException ex) - { - e = ex; - } - if (e != null) + catch (IOException e) { - throw e; } } @@ -322,23 +334,16 @@ class RecordStream return contents; } - private static byte[] doFinal(Digest d) - { - byte[] bs = new byte[d.getDigestSize()]; - d.doFinal(bs, 0); - return bs; - } - private static void checkType(short type, short alertDescription) throws IOException { - switch (type) { - case ContentType.change_cipher_spec: + case ContentType.application_data: case ContentType.alert: + case ContentType.change_cipher_spec: case ContentType.handshake: - case ContentType.application_data: + case ContentType.heartbeat: break; default: throw new TlsFatalAlert(alertDescription); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java index a5d4840..15295ea 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java @@ -1,16 +1,17 @@ package org.bouncycastle.crypto.tls; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Hashtable; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Integers; public abstract class SRPTlsClient extends AbstractTlsClient { - public static final Integer EXT_SRP = Integers.valueOf(ExtensionType.srp); + /** + * @deprecated use TlsSRPUtils.EXT_SRP instead + */ + public static final Integer EXT_SRP = TlsSRPUtils.EXT_SRP; protected byte[] identity; protected byte[] password; @@ -31,40 +32,23 @@ public abstract class SRPTlsClient public int[] getCipherSuites() { - return new int[]{CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA, - CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, - CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,}; + return new int[] { CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA }; } public Hashtable getClientExtensions() throws IOException { - - Hashtable clientExtensions = super.getClientExtensions(); - if (clientExtensions == null) - { - clientExtensions = new Hashtable(); - } - - ByteArrayOutputStream srpData = new ByteArrayOutputStream(); - TlsUtils.writeOpaque8(this.identity, srpData); - clientExtensions.put(EXT_SRP, srpData.toByteArray()); - + Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions()); + TlsSRPUtils.addSRPExtension(clientExtensions, this.identity); return clientExtensions; } public void processServerExtensions(Hashtable serverExtensions) throws IOException { - // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server - if (serverExtensions != null) + if (!TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, TlsSRPUtils.EXT_SRP, AlertDescription.illegal_parameter)) { - byte[] extValue = (byte[])serverExtensions.get(EXT_SRP); - if (extValue != null && extValue.length > 0) - { - throw new TlsFatalAlert(AlertDescription.illegal_parameter); - } + // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java index a7701fe..984246e 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java @@ -1,16 +1,41 @@ package org.bouncycastle.crypto.tls; +import org.bouncycastle.util.Arrays; + public class SecurityParameters { - int entity = -1; - int prfAlgorithm = -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} */ @@ -20,11 +45,11 @@ public class SecurityParameters } /** - * @return {@link PRFAlgorithm} + * @return {@link CipherSuite} */ - public int getPrfAlgorithm() + public int getCipherSuite() { - return prfAlgorithm; + return cipherSuite; } /** @@ -35,6 +60,14 @@ public class SecurityParameters return compressionAlgorithm; } + /** + * @return {@link PRFAlgorithm} + */ + public int getPrfAlgorithm() + { + return prfAlgorithm; + } + public int getVerifyDataLength() { return verifyDataLength; diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java new file mode 100644 index 0000000..c3050f1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java @@ -0,0 +1,63 @@ +package org.bouncycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; + +import org.bouncycastle.crypto.params.DHParameters; +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java new file mode 100644 index 0000000..df9a439 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java @@ -0,0 +1,112 @@ +package org.bouncycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java new file mode 100644 index 0000000..1dc81f0 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java @@ -0,0 +1,86 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java new file mode 100644 index 0000000..68412f8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java @@ -0,0 +1,142 @@ +package org.bouncycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; + +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java index 7ad4644..a5a591e 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java @@ -9,9 +9,8 @@ import java.io.OutputStream; */ public class SignatureAndHashAlgorithm { - - private short hash; - private short signature; + protected short hash; + protected short signature; /** * @param hash {@link HashAlgorithm} @@ -19,7 +18,6 @@ public class SignatureAndHashAlgorithm */ public SignatureAndHashAlgorithm(short hash, short signature) { - if (!TlsUtils.isValidUint8(hash)) { throw new IllegalArgumentException("'hash' should be a uint8"); @@ -65,7 +63,7 @@ public class SignatureAndHashAlgorithm public int hashCode() { - return (getHash() << 8) | getSignature(); + return (getHash() << 16) | getSignature(); } /** diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java new file mode 100644 index 0000000..8293135 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java @@ -0,0 +1,13 @@ +package org.bouncycastle.crypto.tls; + +import java.io.ByteArrayOutputStream; + +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java index 5a71f9b..4080aaa 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java @@ -2,19 +2,18 @@ package org.bouncycastle.crypto.tls; public class SupplementalDataEntry { + protected int dataType; + protected byte[] data; - private int supp_data_type; - private byte[] data; - - public SupplementalDataEntry(int supp_data_type, byte[] data) + public SupplementalDataEntry(int dataType, byte[] data) { - this.supp_data_type = supp_data_type; + this.dataType = dataType; this.data = data; } public int getDataType() { - return supp_data_type; + return dataType; } public byte[] getData() diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java index dbf9d79..bb9306a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java @@ -10,7 +10,6 @@ import org.bouncycastle.util.Arrays; public class TlsAEADCipher implements TlsCipher { - protected TlsContext context; protected int macSize; protected int nonce_explicit_length; @@ -21,11 +20,9 @@ public class TlsAEADCipher protected byte[] encryptImplicitNonce, decryptImplicitNonce; public TlsAEADCipher(TlsContext context, AEADBlockCipher clientWriteCipher, AEADBlockCipher serverWriteCipher, - int cipherKeySize, int macSize) - throws IOException + int cipherKeySize, int macSize) throws IOException { - - if (!ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion())) + if (!TlsUtils.isTLSv12(context)) { throw new TlsFatalAlert(AlertDescription.internal_error); } @@ -33,7 +30,7 @@ public class TlsAEADCipher this.context = context; this.macSize = macSize; - // NOTE: Valid for RFC 5288 ciphers but may need review for other AEAD ciphers + // 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 @@ -94,12 +91,11 @@ public class TlsAEADCipher 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 The nonce_explicit MAY be the 64-bit sequence number. + * RFC 5288/6655 The nonce_explicit MAY be the 64-bit sequence number. * * (May need review for other AEAD ciphers). */ @@ -113,12 +109,13 @@ public class TlsAEADCipher System.arraycopy(nonce, encryptImplicitNonce.length, output, 0, nonce_explicit_length); int outputPos = nonce_explicit_length; - encryptCipher.init(true, - new AEADParameters(null, 8 * macSize, nonce, getAdditionalData(seqNo, type, plaintextLength))); + byte[] additionalData = getAdditionalData(seqNo, type, plaintextLength); + AEADParameters parameters = new AEADParameters(null, 8 * macSize, nonce, additionalData); - outputPos += encryptCipher.processBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos); try { + encryptCipher.init(true, parameters); + outputPos += encryptCipher.processBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos); outputPos += encryptCipher.doFinal(output, outputPos); } catch (Exception e) @@ -138,7 +135,6 @@ public class TlsAEADCipher 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); @@ -155,13 +151,13 @@ public class TlsAEADCipher byte[] output = new byte[plaintextLength]; int outputPos = 0; - decryptCipher.init(false, - new AEADParameters(null, 8 * macSize, nonce, getAdditionalData(seqNo, type, plaintextLength))); - - outputPos += decryptCipher.processBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos); + 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) diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java index 0b218c1..2f9e8a9 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java @@ -16,6 +16,7 @@ import org.bouncycastle.util.Arrays; public class TlsBlockCipher implements TlsCipher { + private static boolean encryptThenMAC = false; protected TlsContext context; protected byte[] randomData; @@ -38,17 +39,14 @@ public class TlsBlockCipher } public TlsBlockCipher(TlsContext context, BlockCipher clientWriteCipher, BlockCipher serverWriteCipher, - Digest clientWriteDigest, Digest serverWriteDigest, int cipherKeySize) - throws IOException + Digest clientWriteDigest, Digest serverWriteDigest, int cipherKeySize) throws IOException { - this.context = context; this.randomData = new byte[256]; context.getSecureRandom().nextBytes(randomData); - this.useExplicitIV = ProtocolVersion.TLSv11.isEqualOrEarlierVersionOf(context.getServerVersion() - .getEquivalentTLSVersion()); + this.useExplicitIV = TlsUtils.isTLSv11(context); int key_block_size = (2 * cipherKeySize) + clientWriteDigest.getDigestSize() + serverWriteDigest.getDigestSize(); @@ -123,13 +121,30 @@ public class TlsBlockCipher int blockSize = encryptCipher.getBlockSize(); int macSize = writeMac.getSize(); - int result = ciphertextLimit - (ciphertextLimit % blockSize) - macSize - 1; + int plaintextLimit = ciphertextLimit; + + // An explicit IV consumes 1 block if (useExplicitIV) { - result -= blockSize; + plaintextLimit -= blockSize; } - return result; + // 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) @@ -139,7 +154,13 @@ public class TlsBlockCipher ProtocolVersion version = context.getServerVersion(); - int padding_length = blockSize - 1 - ((len + macSize) % blockSize); + 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()) @@ -156,7 +177,7 @@ public class TlsBlockCipher totalSize += blockSize; } - byte[] outbuf = new byte[totalSize]; + byte[] outBuf = new byte[totalSize]; int outOff = 0; if (useExplicitIV) @@ -166,25 +187,42 @@ public class TlsBlockCipher encryptCipher.init(true, new ParametersWithIV(null, explicitIV)); - System.arraycopy(explicitIV, 0, outbuf, outOff, blockSize); + System.arraycopy(explicitIV, 0, outBuf, outOff, blockSize); outOff += blockSize; } - byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len); + int blocks_start = outOff; + + System.arraycopy(plaintext, offset, outBuf, outOff, len); + outOff += len; - System.arraycopy(plaintext, offset, outbuf, outOff, len); - System.arraycopy(mac, 0, outbuf, outOff + len, mac.length); + if (!encryptThenMAC) + { + byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len); + System.arraycopy(mac, 0, outBuf, outOff, mac.length); + outOff += mac.length; + } - int padOffset = outOff + len + mac.length; for (int i = 0; i <= padding_length; i++) { - outbuf[i + padOffset] = (byte)padding_length; + outBuf[outOff++] = (byte)padding_length; } - for (int i = outOff; i < totalSize; i += blockSize) + + for (int i = blocks_start; i < outOff; i += blockSize) { - encryptCipher.processBlock(outbuf, i, outbuf, i); + encryptCipher.processBlock(outBuf, i, outBuf, i); } - return outbuf; + + 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) @@ -193,7 +231,16 @@ public class TlsBlockCipher int blockSize = decryptCipher.getBlockSize(); int macSize = readMac.getSize(); - int minLen = Math.max(blockSize, macSize + 1); + int minLen = blockSize; + if (encryptThenMAC) + { + minLen += macSize; + } + else + { + minLen = Math.max(minLen, macSize + 1); + } + if (useExplicitIV) { minLen += blockSize; @@ -204,41 +251,67 @@ public class TlsBlockCipher throw new TlsFatalAlert(AlertDescription.decode_error); } - if (len % blockSize != 0) + 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; - len -= blockSize; + blocks_length -= blockSize; } - for (int i = 0; i < len; i += 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, len, blockSize, macSize); + int totalPad = checkPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMAC ? 0 : macSize); - int macInputLen = len - totalPad - macSize; + int dec_output_length = blocks_length - totalPad; - byte[] decryptedMac = Arrays.copyOfRange(ciphertext, offset + macInputLen, offset + macInputLen + macSize); - byte[] calculatedMac = readMac.calculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen, len - - macSize, randomData); + 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, decryptedMac); + boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac); - if (badMac || totalPad == 0) - { - throw new TlsFatalAlert(AlertDescription.bad_record_mac); + if (badMac || totalPad == 0) + { + throw new TlsFatalAlert(AlertDescription.bad_record_mac); + } } - return Arrays.copyOfRange(ciphertext, offset, offset + macInputLen); + return Arrays.copyOfRange(ciphertext, offset, offset + dec_output_length); } protected int checkPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize) @@ -251,7 +324,7 @@ public class TlsBlockCipher int dummyIndex = 0; byte padDiff = 0; - if ((context.getServerVersion().isSSL() && totalPad > blockSize) || (macSize + totalPad > len)) + if ((TlsUtils.isSSL(context) && totalPad > blockSize) || (macSize + totalPad > len)) { totalPad = 0; } @@ -310,4 +383,4 @@ public class TlsBlockCipher } return n; } -}
\ No newline at end of file +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java index 62444fa..7db86cd 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java @@ -7,9 +7,18 @@ 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(); @@ -25,15 +34,18 @@ public interface TlsClient 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); - void notifySecureRenegotiation(boolean secureNegotiation) - throws IOException; - // Hashtable is (Integer -> byte[]) void processServerExtensions(Hashtable serverExtensions) throws IOException; @@ -52,12 +64,6 @@ public interface TlsClient Vector getClientSupplementalData() throws IOException; - TlsCompression getCompression() - throws IOException; - - TlsCipher getCipher() - throws IOException; - /** * RFC 5077 3.3. NewSessionTicket Handshake Message * <p/> @@ -70,7 +76,4 @@ public interface TlsClient */ void notifyNewSessionTicket(NewSessionTicket newSessionTicket) throws IOException; - - void notifyHandshakeComplete() - throws IOException; } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java index d91f7f8..b320144 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java @@ -6,7 +6,6 @@ class TlsClientContextImpl extends AbstractTlsContext implements TlsClientContext { - TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) { super(secureRandom, securityParameters); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java index 33cd914..506560f 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java @@ -1,7 +1,6 @@ package org.bouncycastle.crypto.tls; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -16,19 +15,15 @@ import org.bouncycastle.util.Arrays; public class TlsClientProtocol extends TlsProtocol { - protected TlsClient tlsClient = null; protected TlsClientContextImpl tlsClientContext = null; - protected int[] offeredCipherSuites = null; - protected short[] offeredCompressionMethods = null; - protected Hashtable clientExtensions = null; - - protected int selectedCipherSuite; - protected short selectedCompressionMethod; + protected byte[] selectedSessionID = null; protected TlsKeyExchange keyExchange = null; protected TlsAuthentication authentication = null; + + protected CertificateStatus certificateStatus = null; protected CertificateRequest certificateRequest = null; private static SecureRandom createSecureRandom() @@ -61,11 +56,10 @@ public class TlsClientProtocol /** * Initiates a TLS handshake in the role of client * - * @param tlsClient + * @param tlsClient The {@link TlsClient} to use for the handshake. * @throws IOException If handshake was not successful. */ - public void connect(TlsClient tlsClient) - throws IOException + public void connect(TlsClient tlsClient) throws IOException { if (tlsClient == null) { @@ -73,7 +67,7 @@ public class TlsClientProtocol } if (this.tlsClient != null) { - throw new IllegalStateException("connect can only be called once"); + throw new IllegalStateException("'connect' can only be called once"); } this.tlsClient = tlsClient; @@ -86,12 +80,32 @@ public class TlsClientProtocol 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(); + } - this.tlsClient.notifyHandshakeComplete(); + protected void cleanupHandshake() + { + super.cleanupHandshake(); + + this.selectedSessionID = null; + this.keyExchange = null; + this.authentication = null; + this.certificateStatus = null; + this.certificateRequest = null; } protected AbstractTlsContext getContext() @@ -104,36 +118,27 @@ public class TlsClientProtocol return tlsClient; } - protected void handleChangeCipherSpecMessage() + protected void handleHandshakeMessage(short type, byte[] data) throws IOException { + ByteArrayInputStream buf = new ByteArrayInputStream(data); - switch (this.connection_state) + if (this.resumedSession) { - case CS_CLIENT_FINISHED: - { - if (this.expectSessionTicket) + if (type != HandshakeType.finished || this.connection_state != CS_SERVER_HELLO) { - /* - * RFC 5077 3.3. This message MUST be sent if the server included a SessionTicket - * extension in the ServerHello. - */ - this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } - // NB: Fall through to next case label - } - case CS_SERVER_SESSION_TICKET: - this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC; - break; - default: - this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); - } - } - protected void handleHandshakeMessage(short type, byte[] data) - throws IOException - { - ByteArrayInputStream buf = new ByteArrayInputStream(data); + processFinishedMessage(buf); + this.connection_state = CS_SERVER_FINISHED; + + sendFinishedMessage(); + this.connection_state = CS_CLIENT_FINISHED; + this.connection_state = CS_END; + + return; + } switch (type) { @@ -150,72 +155,143 @@ public class TlsClientProtocol { // Parse the Certificate message and send to cipher suite - Certificate serverCertificate = Certificate.parse(buf); + this.peerCertificate = Certificate.parse(buf); assertEmpty(buf); - this.keyExchange.processServerCertificate(serverCertificate); + // 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(serverCertificate); + this.authentication.notifyServerCertificate(this.peerCertificate); break; } default: - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + 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_SERVER_CHANGE_CIPHER_SPEC: + case CS_CLIENT_FINISHED: + { processFinishedMessage(buf); this.connection_state = CS_SERVER_FINISHED; + this.connection_state = CS_END; break; + } default: - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + 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; - securityParameters.prfAlgorithm = getPRFAlgorithm(selectedCipherSuite); - securityParameters.compressionAlgorithm = this.selectedCompressionMethod; + 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. */ - securityParameters.verifyDataLength = 12; + this.securityParameters.verifyDataLength = 12; - recordStream.notifyHelloComplete(); + 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: - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; + } case HandshakeType.supplemental_data: { switch (this.connection_state) { case CS_SERVER_HELLO: + { handleSupplementalData(readSupplementalDataMessage(buf)); break; + } default: - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } case HandshakeType.server_hello_done: + { switch (this.connection_state) { case CS_SERVER_HELLO: @@ -225,7 +301,6 @@ public class TlsClientProtocol } case CS_SERVER_SUPPLEMENTAL_DATA: { - // There was no server certificate message; check it's OK this.keyExchange.skipServerCredentials(); this.authentication = null; @@ -233,19 +308,22 @@ public class TlsClientProtocol // 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) { @@ -289,40 +367,56 @@ public class TlsClientProtocol * in our CipherSuite. */ sendClientKeyExchangeMessage(); + this.connection_state = CS_CLIENT_KEY_EXCHANGE; establishMasterSecret(getContext(), keyExchange); + recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher()); - /* - * Initialize our cipher suite - */ - recordStream.setPendingConnectionState(tlsClient.getCompression(), tlsClient.getCipher()); - - this.connection_state = CS_CLIENT_KEY_EXCHANGE; + TlsHandshakeHash prepareFinishHash = recordStream.prepareToFinish(); if (clientCreds != null && clientCreds instanceof TlsSignerCredentials) { + TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds; + /* - * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm - * prepended from TLS 1.2 + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 */ - TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds; - byte[] md5andsha1 = recordStream.getCurrentHash(null); - byte[] clientCertificateSignature = signerCreds.generateCertificateSignature(md5andsha1); - sendCertificateVerifyMessage(clientCertificateSignature); + 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(); - this.connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC; - sendFinishedMessage(); this.connection_state = CS_CLIENT_FINISHED; break; + } default: - this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); + throw new TlsFatalAlert(AlertDescription.handshake_failure); } break; + } case HandshakeType.server_key_exchange: { switch (this.connection_state) @@ -334,7 +428,6 @@ public class TlsClientProtocol } case CS_SERVER_SUPPLEMENTAL_DATA: { - // There was no server certificate message; check it's OK this.keyExchange.skipServerCredentials(); this.authentication = null; @@ -342,14 +435,15 @@ public class TlsClientProtocol // NB: Fall through to next case label } case CS_SERVER_CERTIFICATE: - + case CS_CERTIFICATE_STATUS: + { this.keyExchange.processServerKeyExchange(buf); assertEmpty(buf); break; - + } default: - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } this.connection_state = CS_SERVER_KEY_EXCHANGE; @@ -360,12 +454,13 @@ public class TlsClientProtocol 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) @@ -374,19 +469,26 @@ public class TlsClientProtocol * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server * to request client identification. */ - this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); + throw new TlsFatalAlert(AlertDescription.handshake_failure); } - this.certificateRequest = CertificateRequest.parse(buf); + 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: - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } this.connection_state = CS_CERTIFICATE_REQUEST; @@ -397,23 +499,32 @@ public class TlsClientProtocol 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. */ - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + 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: - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } } case HandshakeType.hello_request: - + { assertEmpty(buf); /* @@ -422,27 +533,34 @@ public class TlsClientProtocol * 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_SERVER_FINISHED) + 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.client_hello: case HandshakeType.hello_verify_request: default: - // We do not support this! - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - break; + throw new TlsFatalAlert(AlertDescription.unexpected_message); } } protected void handleSupplementalData(Vector serverSupplementalData) throws IOException { - this.tlsClient.processServerSupplementalData(serverSupplementalData); this.connection_state = CS_SERVER_SUPPLEMENTAL_DATA; @@ -453,7 +571,6 @@ public class TlsClientProtocol protected void receiveNewSessionTicketMessage(ByteArrayInputStream buf) throws IOException { - NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf); TlsProtocol.assertEmpty(buf); @@ -464,23 +581,22 @@ public class TlsClientProtocol protected void receiveServerHelloMessage(ByteArrayInputStream buf) throws IOException { - ProtocolVersion server_version = TlsUtils.readVersion(buf); if (server_version.isDTLS()) { - this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } // Check that this matches what the server is sending in the record layer - if (!server_version.equals(recordStream.getReadVersion())) + if (!server_version.equals(this.recordStream.getReadVersion())) { - this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } ProtocolVersion client_version = getContext().getClientVersion(); if (!server_version.isEqualOrEarlierVersionOf(client_version)) { - this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.recordStream.setWriteVersion(server_version); @@ -490,38 +606,41 @@ public class TlsClientProtocol /* * Read the server random */ - securityParameters.serverRandom = TlsUtils.readFully(32, buf); + this.securityParameters.serverRandom = TlsUtils.readFully(32, buf); - byte[] sessionID = TlsUtils.readOpaque8(buf); - if (sessionID.length > 32) + this.selectedSessionID = TlsUtils.readOpaque8(buf); + if (this.selectedSessionID.length > 32) { - this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - this.tlsClient.notifySessionID(sessionID); + 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. */ - this.selectedCipherSuite = TlsUtils.readUint16(buf); - if (!arrayContains(offeredCipherSuites, this.selectedCipherSuite) - || this.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL - || this.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + 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) { - this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - this.tlsClient.notifySelectedCipherSuite(this.selectedCipherSuite); + 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 (!arrayContains(offeredCompressionMethods, selectedCompressionMethod)) + if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod)) { - this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } this.tlsClient.notifySelectedCompressionMethod(selectedCompressionMethod); @@ -534,15 +653,7 @@ public class TlsClientProtocol * 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 = readExtensions(buf); + this.serverExtensions = readExtensions(buf); /* * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an @@ -551,9 +662,9 @@ public class TlsClientProtocol * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server * Hello is always allowed. */ - if (serverExtensions != null) + if (this.serverExtensions != null) { - Enumeration e = serverExtensions.keys(); + Enumeration e = this.serverExtensions.keys(); while (e.hasMoreElements()) { Integer extType = (Integer)e.nextElement(); @@ -565,107 +676,169 @@ public class TlsClientProtocol * 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) - && (clientExtensions == null || clientExtensions.get(extType) == null)) + if (extType.equals(EXT_RenegotiationInfo)) { - /* - * 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. - */ - this.failWithError(AlertLevel.fatal, AlertDescription.unsupported_extension); + 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 + */ + { /* - * 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) { /* - * When a ServerHello is received, the client MUST check if it includes the - * "renegotiation_info" extension: + * 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). */ - byte[] renegExtValue = (byte[])serverExtensions.get(EXT_RenegotiationInfo); - if (renegExtValue != 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; + this.secure_renegotiation = true; - if (!Arrays.constantTimeAreEqual(renegExtValue, createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) - { - this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); - } + 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); - this.expectSessionTicket = serverExtensions.containsKey(EXT_SessionTicket); + 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(); } - tlsClient.notifySecureRenegotiation(this.secure_renegotiation); + this.securityParameters.cipherSuite = selectedCipherSuite; + this.securityParameters.compressionAlgorithm = selectedCompressionMethod; - if (clientExtensions != null) + 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) { - tlsClient.processServerExtensions(serverExtensions); + this.tlsClient.processServerExtensions(sessionServerExtensions); } } - protected void sendCertificateVerifyMessage(byte[] data) + protected void sendCertificateVerifyMessage(DigitallySigned certificateVerify) throws IOException { - /* - * Send signature of handshake messages so far to prove we are the owner of the cert See RFC - * 2246 sections 4.7, 7.4.3 and 7.4.8 - */ - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - TlsUtils.writeUint8(HandshakeType.certificate_verify, bos); - TlsUtils.writeUint24(data.length + 2, bos); - TlsUtils.writeOpaque16(data, bos); - byte[] message = bos.toByteArray(); + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify); + + certificateVerify.encode(message); - safeWriteRecord(ContentType.handshake, message, 0, message.length); + message.writeToRecordStream(); } protected void sendClientHelloMessage() throws IOException { - - recordStream.setWriteVersion(this.tlsClient.getClientHelloRecordLayerVersion()); - - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeUint8(HandshakeType.client_hello, buf); - - // Reserve space for length - TlsUtils.writeUint24(0, buf); + this.recordStream.setWriteVersion(this.tlsClient.getClientHelloRecordLayerVersion()); ProtocolVersion client_version = this.tlsClient.getClientVersion(); if (client_version.isDTLS()) { - this.failWithError(AlertLevel.fatal, AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error); } getContext().setClientVersion(client_version); - TlsUtils.writeVersion(client_version, buf); - - buf.write(securityParameters.clientRandom); - - // Session id - TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf); /* - * Cipher suites + * 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(); - // Integer -> byte[] + 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) { /* @@ -673,60 +846,39 @@ public class TlsClientProtocol * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the * ClientHello. Including both is NOT RECOMMENDED. */ - boolean noRenegExt = clientExtensions == null || clientExtensions.get(EXT_RenegotiationInfo) == null; + byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo); + boolean noRenegExt = (null == renegExtData); - int count = offeredCipherSuites.length; - if (noRenegExt) - { - // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV - ++count; - } + boolean noSCSV = !Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV); - TlsUtils.writeUint16(2 * count, buf); - TlsUtils.writeUint16Array(offeredCipherSuites, buf); - - if (noRenegExt) + if (noRenegExt && noSCSV) { - TlsUtils.writeUint16(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, buf); + // 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); } - } - // Compression methods - this.offeredCompressionMethods = this.tlsClient.getCompressionMethods(); + TlsUtils.writeUint16ArrayWithUint16Length(offeredCipherSuites, message); + } - TlsUtils.writeUint8((short)offeredCompressionMethods.length, buf); - TlsUtils.writeUint8Array(offeredCompressionMethods, buf); + TlsUtils.writeUint8ArrayWithUint8Length(offeredCompressionMethods, message); - // Extensions if (clientExtensions != null) { - writeExtensions(buf, clientExtensions); + writeExtensions(message, clientExtensions); } - byte[] message = buf.toByteArray(); - - // Patch actual length back in - TlsUtils.writeUint24(message.length - 4, message, 1); - - safeWriteRecord(ContentType.handshake, message, 0, message.length); + message.writeToRecordStream(); } protected void sendClientKeyExchangeMessage() throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - TlsUtils.writeUint8(HandshakeType.client_key_exchange, bos); - - // Reserve space for length - TlsUtils.writeUint24(0, bos); - - this.keyExchange.generateClientKeyExchange(bos); - byte[] message = bos.toByteArray(); + HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange); - // Patch actual length back in - TlsUtils.writeUint24(message.length - 4, message, 1); + this.keyExchange.generateClientKeyExchange(message); - safeWriteRecord(ContentType.handshake, message, 0, message.length); + message.writeToRecordStream(); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java index dfb1052..04781ef 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java @@ -4,7 +4,6 @@ import java.security.SecureRandom; public interface TlsContext { - SecureRandom getSecureRandom(); SecurityParameters getSecurityParameters(); @@ -15,6 +14,16 @@ public interface TlsContext 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); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java index 5737659..0abaee6 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java @@ -1,24 +1,17 @@ package org.bouncycastle.crypto.tls; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; -import java.math.BigInteger; import java.util.Vector; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; -import org.bouncycastle.crypto.generators.DHKeyPairGenerator; -import org.bouncycastle.crypto.io.SignerInputStream; -import org.bouncycastle.crypto.params.DHKeyGenerationParameters; import org.bouncycastle.crypto.params.DHParameters; -import org.bouncycastle.crypto.params.DHPublicKeyParameters; +import org.bouncycastle.util.io.TeeInputStream; public class TlsDHEKeyExchange extends TlsDHKeyExchange { - protected TlsSignerCredentials serverCredentials = null; public TlsDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters) @@ -29,7 +22,6 @@ public class TlsDHEKeyExchange public void processServerCredentials(TlsCredentials serverCredentials) throws IOException { - if (!(serverCredentials instanceof TlsSignerCredentials)) { throw new TlsFatalAlert(AlertDescription.internal_error); @@ -43,40 +35,50 @@ public class TlsDHEKeyExchange public byte[] generateServerKeyExchange() throws IOException { - if (this.dhParameters == null) { throw new TlsFatalAlert(AlertDescription.internal_error); } - ByteArrayOutputStream buf = new ByteArrayOutputStream(); + DigestInputBuffer buf = new DigestInputBuffer(); - DHKeyPairGenerator kpg = new DHKeyPairGenerator(); - kpg.init(new DHKeyGenerationParameters(context.getSecureRandom(), this.dhParameters)); - AsymmetricCipherKeyPair kp = kpg.generateKeyPair(); + this.dhAgreeServerPrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(), + this.dhParameters, buf); - BigInteger Ys = ((DHPublicKeyParameters)kp.getPublic()).getY(); + /* + * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 + */ + SignatureAndHashAlgorithm signatureAndHashAlgorithm; + Digest d; - TlsDHUtils.writeDHParameter(dhParameters.getP(), buf); - TlsDHUtils.writeDHParameter(dhParameters.getG(), buf); - TlsDHUtils.writeDHParameter(Ys, buf); + if (TlsUtils.isTLSv12(context)) + { + signatureAndHashAlgorithm = serverCredentials.getSignatureAndHashAlgorithm(); + if (signatureAndHashAlgorithm == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } - byte[] digestInput = buf.toByteArray(); + d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash()); + } + else + { + signatureAndHashAlgorithm = null; + d = new CombinedHash(); + } - Digest d = new CombinedHash(); SecurityParameters securityParameters = context.getSecurityParameters(); d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length); d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length); - d.update(digestInput, 0, digestInput.length); + buf.updateDigest(d); byte[] hash = new byte[d.getDigestSize()]; d.doFinal(hash, 0); - byte[] sigBytes = serverCredentials.generateCertificateSignature(hash); - /* - * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended from TLS 1.2 - */ - TlsUtils.writeOpaque16(sigBytes, buf); + byte[] signature = serverCredentials.generateCertificateSignature(hash); + + DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature); + signed_params.encode(buf); return buf.toByteArray(); } @@ -84,28 +86,28 @@ public class TlsDHEKeyExchange public void processServerKeyExchange(InputStream input) throws IOException { - SecurityParameters securityParameters = context.getSecurityParameters(); - Signer signer = initVerifyer(tlsSigner, securityParameters); - InputStream sigIn = new SignerInputStream(input, signer); + SignerInputBuffer buf = new SignerInputBuffer(); + InputStream teeIn = new TeeInputStream(input, buf); + + ServerDHParams params = ServerDHParams.parse(teeIn); - BigInteger p = TlsDHUtils.readDHParameter(sigIn); - BigInteger g = TlsDHUtils.readDHParameter(sigIn); - BigInteger Ys = TlsDHUtils.readDHParameter(sigIn); + DigitallySigned signed_params = DigitallySigned.parse(context, input); - byte[] sigBytes = TlsUtils.readOpaque16(input); - if (!signer.verifySignature(sigBytes)) + 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 = validateDHPublicKey(new DHPublicKeyParameters(Ys, new DHParameters(p, g))); + this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey(params.getPublicKey()); } - protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters) + protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters) { - Signer signer = tlsSigner.createVerifyer(this.serverPublicKey); + 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java index 60e5105..7d79f6a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java @@ -7,7 +7,6 @@ import java.util.Vector; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.DHParameters; import org.bouncycastle.crypto.params.DHPrivateKeyParameters; @@ -20,7 +19,6 @@ import org.bouncycastle.crypto.util.PublicKeyFactory; public class TlsDHKeyExchange extends AbstractTlsKeyExchange { - protected static final BigInteger ONE = BigInteger.valueOf(1); protected static final BigInteger TWO = BigInteger.valueOf(2); @@ -32,11 +30,11 @@ public class TlsDHKeyExchange 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) @@ -77,7 +75,6 @@ public class TlsDHKeyExchange public void processServerCertificate(Certificate serverCertificate) throws IOException { - if (serverCertificate.isEmpty()) { throw new TlsFatalAlert(AlertDescription.bad_certificate); @@ -99,7 +96,7 @@ public class TlsDHKeyExchange { try { - this.dhAgreeServerPublicKey = validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey); + this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey); } catch (ClassCastException e) { @@ -196,27 +193,16 @@ public class TlsDHKeyExchange return agreementCredentials.generateAgreement(dhAgreeServerPublicKey); } - return calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey); - } - - protected boolean areCompatibleParameters(DHParameters a, DHParameters b) - { - return a.getP().equals(b.getP()) && a.getG().equals(b.getG()); - } - - protected byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey, DHPrivateKeyParameters privateKey) - { - return TlsDHUtils.calculateDHBasicAgreement(publicKey, privateKey); - } + if (dhAgreeServerPrivateKey != null) + { + return TlsDHUtils.calculateDHBasicAgreement(dhAgreeClientPublicKey, dhAgreeServerPrivateKey); + } - protected AsymmetricCipherKeyPair generateDHKeyPair(DHParameters dhParams) - { - return TlsDHUtils.generateDHKeyPair(context.getSecureRandom(), dhParams); - } + if (dhAgreeClientPrivateKey != null) + { + return TlsDHUtils.calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey); + } - protected DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key) - throws IOException - { - return TlsDHUtils.validateDHPublicKey(key); + throw new TlsFatalAlert(AlertDescription.internal_error); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java index 014e40f..748c879 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java @@ -17,14 +17,16 @@ import org.bouncycastle.util.BigIntegers; public class TlsDHUtils { - static final BigInteger ONE = BigInteger.valueOf(1); static final BigInteger TWO = BigInteger.valueOf(2); - public static byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey, - DHPrivateKeyParameters privateKey) + 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); @@ -36,32 +38,37 @@ public class TlsDHUtils return BigIntegers.asUnsignedByteArray(agreementValue); } - public static AsymmetricCipherKeyPair generateDHKeyPair(SecureRandom random, - DHParameters dhParams) + 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 + 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); - AsymmetricCipherKeyPair dhAgreeClientKeyPair = generateDHKeyPair(random, dhParams); - DHPrivateKeyParameters dhAgreeClientPrivateKey = (DHPrivateKeyParameters)dhAgreeClientKeyPair - .getPrivate(); + return (DHPrivateKeyParameters) kp.getPrivate(); + } + + public static DHPrivateKeyParameters generateEphemeralServerKeyExchange(SecureRandom random, DHParameters dhParams, + OutputStream output) throws IOException + { + AsymmetricCipherKeyPair kp = TlsDHUtils.generateDHKeyPair(random, dhParams); - BigInteger Yc = ((DHPublicKeyParameters)dhAgreeClientKeyPair.getPublic()).getY(); - byte[] keData = BigIntegers.asUnsignedByteArray(Yc); - TlsUtils.writeOpaque16(keData, output); + DHPublicKeyParameters dhPublicKey = (DHPublicKeyParameters)kp.getPublic(); + ServerDHParams params = new ServerDHParams(dhPublicKey); + params.encode(output); - return dhAgreeClientPrivateKey; + return (DHPrivateKeyParameters)kp.getPrivate(); } - public static DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key) - throws IOException + public static DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key) throws IOException { BigInteger Y = key.getY(); DHParameters params = key.getParameters(); @@ -86,14 +93,12 @@ public class TlsDHUtils return key; } - public static BigInteger readDHParameter(InputStream input) - throws IOException + 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 + public static void writeDHParameter(BigInteger x, OutputStream output) throws IOException { TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(x), output); } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java index b0e8957..4cb8004 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java @@ -6,7 +6,6 @@ import org.bouncycastle.crypto.DSA; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; import org.bouncycastle.crypto.digests.NullDigest; -import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.signers.DSADigestSigner; @@ -14,44 +13,73 @@ import org.bouncycastle.crypto.signers.DSADigestSigner; public abstract class TlsDSASigner extends AbstractTlsSigner { - - public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1) + public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) throws CryptoException { - - // Note: Only use the SHA1 part of the hash - Signer signer = makeSigner(new NullDigest(), true, + Signer signer = makeSigner(algorithm, true, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom())); - signer.update(md5AndSha1, 16, 20); + 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(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1) + public boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) throws CryptoException { - - // Note: Only use the SHA1 part of the hash - Signer signer = makeSigner(new NullDigest(), false, publicKey); - signer.update(md5AndSha1, 16, 20); + 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(AsymmetricKeyParameter privateKey) + public Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) { - return makeSigner(new SHA1Digest(), true, new ParametersWithRandom(privateKey, this.context.getSecureRandom())); + return makeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom())); } - public Signer createVerifyer(AsymmetricKeyParameter publicKey) + public Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) { - return makeSigner(new SHA1Digest(), false, publicKey); + return makeSigner(algorithm, false, false, publicKey); } - protected Signer makeSigner(Digest d, boolean forSigning, CipherParameters cp) + 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java index e0eeca9..cb698bf 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java @@ -8,7 +8,6 @@ import org.bouncycastle.crypto.signers.DSASigner; public class TlsDSSSigner extends TlsDSASigner { - public boolean isValidPublicKey(AsymmetricKeyParameter publicKey) { return publicKey instanceof DSAPublicKeyParameters; @@ -18,4 +17,9 @@ public class TlsDSSSigner { return new DSASigner(); } + + protected short getSignatureAlgorithm() + { + return SignatureAlgorithm.dsa; + } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java index a49f83f..3e7ef39 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java @@ -1,7 +1,6 @@ package org.bouncycastle.crypto.tls; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -9,7 +8,7 @@ import java.math.BigInteger; import java.security.SecureRandom; import java.util.Hashtable; -import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; @@ -19,91 +18,63 @@ import org.bouncycastle.crypto.params.ECKeyGenerationParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.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", + 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",}; + "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1", + "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"}; - public static void addSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves) - throws IOException + 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 + public static int[] getSupportedEllipticCurvesExtension(Hashtable extensions) throws IOException { - - if (extensions == null) - { - return null; - } - byte[] extensionValue = (byte[])extensions.get(EXT_elliptic_curves); - if (extensionValue == null) - { - return null; - } - return readSupportedEllipticCurvesExtension(extensionValue); + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_elliptic_curves); + return extensionData == null ? null : readSupportedEllipticCurvesExtension(extensionData); } - public static short[] getSupportedPointFormatsExtension(Hashtable extensions) - throws IOException + public static short[] getSupportedPointFormatsExtension(Hashtable extensions) throws IOException { - - if (extensions == null) - { - return null; - } - byte[] extensionValue = (byte[])extensions.get(EXT_ec_point_formats); - if (extensionValue == null) - { - return null; - } - return readSupportedPointFormatsExtension(extensionValue); + byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_ec_point_formats); + return extensionData == null ? null : readSupportedPointFormatsExtension(extensionData); } - public static byte[] createSupportedEllipticCurvesExtension(int[] namedCurves) - throws IOException + public static byte[] createSupportedEllipticCurvesExtension(int[] namedCurves) throws IOException { - if (namedCurves == null || namedCurves.length < 1) { throw new TlsFatalAlert(AlertDescription.internal_error); } - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeUint16(2 * namedCurves.length, buf); - TlsUtils.writeUint16Array(namedCurves, buf); - return buf.toByteArray(); + return TlsUtils.encodeUint16ArrayWithUint16Length(namedCurves); } - public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats) - throws IOException + public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats) throws IOException { - if (ecPointFormats == null) { - ecPointFormats = new short[]{ECPointFormat.uncompressed}; + ecPointFormats = new short[] { ECPointFormat.uncompressed }; } - else if (!TlsProtocol.arrayContains(ecPointFormats, ECPointFormat.uncompressed)) + else if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed)) { /* * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST @@ -118,22 +89,17 @@ public class TlsECCUtils ecPointFormats = tmp; } - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeUint8((short)ecPointFormats.length, buf); - TlsUtils.writeUint8Array(ecPointFormats, buf); - return buf.toByteArray(); + return TlsUtils.encodeUint8ArrayWithUint8Length(ecPointFormats); } - public static int[] readSupportedEllipticCurvesExtension(byte[] extensionValue) - throws IOException + public static int[] readSupportedEllipticCurvesExtension(byte[] extensionData) throws IOException { - - if (extensionValue == null) + if (extensionData == null) { - throw new IllegalArgumentException("'extensionValue' cannot be null"); + throw new IllegalArgumentException("'extensionData' cannot be null"); } - ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue); + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); int length = TlsUtils.readUint16(buf); if (length < 2 || (length & 1) != 0) @@ -148,16 +114,14 @@ public class TlsECCUtils return namedCurves; } - public static short[] readSupportedPointFormatsExtension(byte[] extensionValue) - throws IOException + public static short[] readSupportedPointFormatsExtension(byte[] extensionData) throws IOException { - - if (extensionValue == null) + if (extensionData == null) { - throw new IllegalArgumentException("'extensionValue' cannot be null"); + throw new IllegalArgumentException("'extensionData' cannot be null"); } - ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue); + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); short length = TlsUtils.readUint8(buf); if (length < 1) @@ -169,7 +133,7 @@ public class TlsECCUtils TlsProtocol.assertEmpty(buf); - if (!TlsProtocol.arrayContains(ecPointFormats, ECPointFormat.uncompressed)) + if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed)) { /* * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST @@ -195,7 +159,7 @@ public class TlsECCUtils } // Lazily created the first time a particular curve is accessed - X9ECParameters ecP = SECNamedCurves.getByName(curveName); + X9ECParameters ecP = ECNamedCurveTable.getByName(curveName); if (ecP == null) { @@ -227,6 +191,9 @@ public class TlsECCUtils { 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: @@ -252,6 +219,10 @@ public class TlsECCUtils 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: @@ -268,7 +239,38 @@ public class TlsECCUtils 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; } @@ -307,17 +309,13 @@ public class TlsECCUtils return false; } - public static byte[] serializeECFieldElement(int fieldSize, BigInteger x) - throws IOException + public static byte[] serializeECFieldElement(int fieldSize, BigInteger x) throws IOException { - int requiredLength = (fieldSize + 7) / 8; - return BigIntegers.asUnsignedByteArray(requiredLength, x); + return BigIntegers.asUnsignedByteArray((fieldSize + 7) / 8, x); } - public static byte[] serializeECPoint(short[] ecPointFormats, ECPoint point) - throws IOException + public static byte[] serializeECPoint(short[] ecPointFormats, ECPoint point) throws IOException { - ECCurve curve = point.getCurve(); /* @@ -341,12 +339,10 @@ public class TlsECCUtils 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 + public static BigInteger deserializeECFieldElement(int fieldSize, byte[] encoding) throws IOException { int requiredLength = (fieldSize + 7) / 8; if (encoding.length != requiredLength) @@ -356,22 +352,20 @@ public class TlsECCUtils return new BigInteger(1, encoding); } - public static ECPoint deserializeECPoint(short[] ecPointFormats, ECCurve curve, byte[] encoding) - throws IOException + 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 + byte[] encoding) throws IOException { - try { ECPoint Y = deserializeECPoint(ecPointFormats, curve_params.getCurve(), encoding); @@ -385,7 +379,6 @@ public class TlsECCUtils public static byte[] calculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey) { - ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); basicAgreement.init(privateKey); BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey); @@ -400,22 +393,29 @@ public class TlsECCUtils public static AsymmetricCipherKeyPair generateECKeyPair(SecureRandom random, ECDomainParameters ecParams) { - ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); - ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams, random); - keyPairGenerator.init(keyGenerationParameters); + keyPairGenerator.init(new ECKeyGenerationParameters(ecParams, random)); return keyPairGenerator.generateKeyPair(); } - public static ECPublicKeyParameters validateECPublicKey(ECPublicKeyParameters key) - throws IOException + 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 + public static int readECExponent(int fieldSize, InputStream input) throws IOException { BigInteger K = readECParameter(input); if (K.bitLength() < 32) @@ -429,14 +429,12 @@ public class TlsECCUtils throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - public static BigInteger readECFieldElement(int fieldSize, InputStream input) - throws IOException + public static BigInteger readECFieldElement(int fieldSize, InputStream input) throws IOException { return deserializeECFieldElement(fieldSize, TlsUtils.readOpaque8(input)); } - public static BigInteger readECParameter(InputStream input) - throws IOException + public static BigInteger readECParameter(InputStream input) throws IOException { // TODO Are leading zeroes okay here? return new BigInteger(1, TlsUtils.readOpaque8(input)); @@ -445,7 +443,6 @@ public class TlsECCUtils public static ECDomainParameters readECParameters(int[] namedCurves, short[] ecPointFormats, InputStream input) throws IOException { - try { short curveType = TlsUtils.readUint8(input); @@ -454,6 +451,8 @@ public class TlsECCUtils { 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); @@ -465,11 +464,12 @@ public class TlsECCUtils } 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) - { + switch (basis) { case ECBasisType.ec_basis_trinomial: { int k = readECExponent(m, input); @@ -509,15 +509,7 @@ public class TlsECCUtils throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - if (!TlsProtocol.arrayContains(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); - } + checkNamedCurve(namedCurves, namedCurve); return TlsECCUtils.getParametersForNamedCurve(namedCurve); } @@ -531,47 +523,59 @@ public class TlsECCUtils } } - public static void writeECExponent(int k, OutputStream output) - throws IOException + 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(int fieldSize, BigInteger x, OutputStream output) - throws IOException + 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 + 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 + 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; + 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; - TlsUtils.writeUint16(f2m.getM(), output); + ECCurve.F2m f2m = (ECCurve.F2m) curve; + int m = f2m.getM(); + TlsUtils.checkUint16(m); + TlsUtils.writeUint16(m, output); if (f2m.isTrinomial()) { @@ -592,17 +596,20 @@ public class TlsECCUtils throw new IllegalArgumentException("'ecParameters' not a known curve type"); } - writeECFieldElement(curve.getFieldSize(), curve.getA().toBigInteger(), output); - writeECFieldElement(curve.getFieldSize(), curve.getB().toBigInteger(), output); + 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 writeNamedECParameters(int namedCurve, OutputStream output) - throws IOException + 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)) { /* @@ -614,6 +621,7 @@ public class TlsECCUtils } TlsUtils.writeUint8(ECCurveType.named_curve, output); + TlsUtils.checkUint16(namedCurve); TlsUtils.writeUint16(namedCurve, output); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java index 1124560..d346ef4 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.tls; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Vector; @@ -8,10 +7,11 @@ import java.util.Vector; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; -import org.bouncycastle.crypto.io.SignerInputStream; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.io.TeeInputStream; /** * ECDHE key exchange (see RFC 4492) @@ -19,11 +19,10 @@ import org.bouncycastle.crypto.params.ECPublicKeyParameters; public class TlsECDHEKeyExchange extends TlsECDHKeyExchange { - protected TlsSignerCredentials serverCredentials = null; public TlsECDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves, - short[] clientECPointFormats, short[] serverECPointFormats) + short[] clientECPointFormats, short[] serverECPointFormats) { super(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, serverECPointFormats); } @@ -31,7 +30,6 @@ public class TlsECDHEKeyExchange public void processServerCredentials(TlsCredentials serverCredentials) throws IOException { - if (!(serverCredentials instanceof TlsSignerCredentials)) { throw new TlsFatalAlert(AlertDescription.internal_error); @@ -45,13 +43,13 @@ public class TlsECDHEKeyExchange 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 @@ -77,13 +75,13 @@ public class TlsECDHEKeyExchange /* * If no named curves are suitable, check if the client supports explicit curves. */ - if (TlsProtocol.arrayContains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves)) + if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves)) { curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.secp256r1); } - else if (TlsProtocol.arrayContains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves)) + else if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves)) { - curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.sect233r1); + curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.sect283r1); } } @@ -97,12 +95,9 @@ public class TlsECDHEKeyExchange } AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(context.getSecureRandom(), curve_params); - this.ecAgreeServerPrivateKey = (ECPrivateKeyParameters)kp.getPrivate(); + this.ecAgreePrivateKey = (ECPrivateKeyParameters)kp.getPrivate(); - byte[] publicBytes = TlsECCUtils.serializeECPublicKey(clientECPointFormats, - (ECPublicKeyParameters)kp.getPublic()); - - ByteArrayOutputStream buf = new ByteArrayOutputStream(); + DigestInputBuffer buf = new DigestInputBuffer(); if (namedCurve < 0) { @@ -113,25 +108,43 @@ public class TlsECDHEKeyExchange TlsECCUtils.writeNamedECParameters(namedCurve, buf); } - TlsUtils.writeOpaque8(publicBytes, 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); + } - byte[] digestInput = buf.toByteArray(); + d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash()); + } + else + { + signatureAndHashAlgorithm = null; + d = new CombinedHash(); + } - Digest d = new CombinedHash(); SecurityParameters securityParameters = context.getSecurityParameters(); d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length); d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length); - d.update(digestInput, 0, digestInput.length); + buf.updateDigest(d); byte[] hash = new byte[d.getDigestSize()]; d.doFinal(hash, 0); - byte[] sigBytes = serverCredentials.generateCertificateSignature(hash); - /* - * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended - * from TLS 1.2 - */ - TlsUtils.writeOpaque16(sigBytes, buf); + byte[] signature = serverCredentials.generateCertificateSignature(hash); + + DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature); + signed_params.encode(buf); return buf.toByteArray(); } @@ -139,23 +152,25 @@ public class TlsECDHEKeyExchange public void processServerKeyExchange(InputStream input) throws IOException { - SecurityParameters securityParameters = context.getSecurityParameters(); - Signer signer = initVerifyer(tlsSigner, securityParameters); - InputStream sigIn = new SignerInputStream(input, signer); + SignerInputBuffer buf = new SignerInputBuffer(); + InputStream teeIn = new TeeInputStream(input, buf); + + ECDomainParameters curve_params = TlsECCUtils.readECParameters(namedCurves, clientECPointFormats, teeIn); - ECDomainParameters curve_params = TlsECCUtils.readECParameters(namedCurves, clientECPointFormats, sigIn); + byte[] point = TlsUtils.readOpaque8(teeIn); - byte[] point = TlsUtils.readOpaque8(sigIn); + DigitallySigned signed_params = DigitallySigned.parse(context, input); - byte[] sigByte = TlsUtils.readOpaque16(input); - if (!signer.verifySignature(sigByte)) + Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters); + buf.updateSigner(signer); + if (!signer.verifySignature(signed_params.getSignature())) { throw new TlsFatalAlert(AlertDescription.decrypt_error); } - this.ecAgreeServerPublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey( + this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey( clientECPointFormats, curve_params, point)); } @@ -196,9 +211,9 @@ public class TlsECDHEKeyExchange } } - protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters) + protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters) { - Signer signer = tlsSigner.createVerifyer(this.serverPublicKey); + 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java index 26c0975..9456352 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java @@ -7,7 +7,6 @@ import java.util.Vector; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; @@ -17,26 +16,21 @@ import org.bouncycastle.crypto.util.PublicKeyFactory; /** * ECDH key exchange (see RFC 4492) */ -public class TlsECDHKeyExchange - extends AbstractTlsKeyExchange +public class TlsECDHKeyExchange extends AbstractTlsKeyExchange { - protected TlsSigner tlsSigner; protected int[] namedCurves; protected short[] clientECPointFormats, serverECPointFormats; protected AsymmetricKeyParameter serverPublicKey; - protected ECPublicKeyParameters ecAgreeServerPublicKey; protected TlsAgreementCredentials agreementCredentials; - protected ECPrivateKeyParameters ecAgreeClientPrivateKey; - protected ECPrivateKeyParameters ecAgreeServerPrivateKey; - protected ECPublicKeyParameters ecAgreeClientPublicKey; + protected ECPrivateKeyParameters ecAgreePrivateKey; + protected ECPublicKeyParameters ecAgreePublicKey; public TlsECDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves, - short[] clientECPointFormats, short[] serverECPointFormats) + short[] clientECPointFormats, short[] serverECPointFormats) { - super(keyExchange, supportedSignatureAlgorithms); switch (keyExchange) @@ -71,16 +65,13 @@ public class TlsECDHKeyExchange } } - public void skipServerCredentials() - throws IOException + public void skipServerCredentials() throws IOException { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - public void processServerCertificate(Certificate serverCertificate) - throws IOException + public void processServerCertificate(Certificate serverCertificate) throws IOException { - if (serverCertificate.isEmpty()) { throw new TlsFatalAlert(AlertDescription.bad_certificate); @@ -102,8 +93,7 @@ public class TlsECDHKeyExchange { try { - this.ecAgreeServerPublicKey = TlsECCUtils - .validateECPublicKey((ECPublicKeyParameters)this.serverPublicKey); + this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey((ECPublicKeyParameters) this.serverPublicKey); } catch (ClassCastException e) { @@ -138,8 +128,7 @@ public class TlsECDHKeyExchange } } - public void validateCertificateRequest(CertificateRequest certificateRequest) - throws IOException + public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException { /* * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with @@ -164,14 +153,13 @@ public class TlsECDHKeyExchange } } - public void processClientCredentials(TlsCredentials clientCredentials) - throws IOException + 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; + this.agreementCredentials = (TlsAgreementCredentials) clientCredentials; } else if (clientCredentials instanceof TlsSignerCredentials) { @@ -183,37 +171,24 @@ public class TlsECDHKeyExchange } } - public void generateClientKeyExchange(OutputStream output) - throws IOException + public void generateClientKeyExchange(OutputStream output) throws IOException { - if (agreementCredentials != null) + if (agreementCredentials == null) { - return; + this.ecAgreePrivateKey = TlsECCUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(), + serverECPointFormats, ecAgreePublicKey.getParameters(), output); } - - AsymmetricCipherKeyPair ecAgreeClientKeyPair = TlsECCUtils.generateECKeyPair(context.getSecureRandom(), - ecAgreeServerPublicKey.getParameters()); - this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.getPrivate(); - - byte[] point = TlsECCUtils.serializeECPublicKey(serverECPointFormats, - (ECPublicKeyParameters)ecAgreeClientKeyPair.getPublic()); - - TlsUtils.writeOpaque8(point, output); } - public void processClientCertificate(Certificate clientCertificate) - throws IOException + 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 + public void processClientKeyExchange(InputStream input) throws IOException { - - if (ecAgreeClientPublicKey != null) + if (ecAgreePublicKey != null) { // For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate return; @@ -221,28 +196,22 @@ public class TlsECDHKeyExchange byte[] point = TlsUtils.readOpaque8(input); - ECDomainParameters curve_params = this.ecAgreeServerPrivateKey.getParameters(); + ECDomainParameters curve_params = this.ecAgreePrivateKey.getParameters(); - this.ecAgreeClientPublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey( + this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey( serverECPointFormats, curve_params, point)); } - public byte[] generatePremasterSecret() - throws IOException + public byte[] generatePremasterSecret() throws IOException { if (agreementCredentials != null) { - return agreementCredentials.generateAgreement(ecAgreeServerPublicKey); - } - - if (ecAgreeServerPrivateKey != null) - { - return TlsECCUtils.calculateECDHBasicAgreement(ecAgreeClientPublicKey, ecAgreeServerPrivateKey); + return agreementCredentials.generateAgreement(ecAgreePublicKey); } - if (ecAgreeClientPrivateKey != null) + if (ecAgreePrivateKey != null) { - return TlsECCUtils.calculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey); + return TlsECCUtils.calculateECDHBasicAgreement(ecAgreePublicKey, ecAgreePrivateKey); } throw new TlsFatalAlert(AlertDescription.internal_error); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java index 6809815..d7f8064 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java @@ -8,7 +8,6 @@ import org.bouncycastle.crypto.signers.ECDSASigner; public class TlsECDSASigner extends TlsDSASigner { - public boolean isValidPublicKey(AsymmetricKeyParameter publicKey) { return publicKey instanceof ECPublicKeyParameters; @@ -18,4 +17,9 @@ public class TlsECDSASigner { return new ECDSASigner(); } + + protected short getSignatureAlgorithm() + { + return SignatureAlgorithm.ecdsa; + } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java index 2680136..eddf684 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java @@ -5,7 +5,6 @@ import java.io.IOException; public interface TlsEncryptionCredentials extends TlsCredentials { - byte[] decryptPreMasterSecret(byte[] encryptedPreMasterSecret) throws IOException; } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java new file mode 100644 index 0000000..fbc39dd --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java @@ -0,0 +1,240 @@ +package org.bouncycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; + +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java index b17b8d7..1cb0f4d 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java @@ -5,10 +5,17 @@ import org.bouncycastle.crypto.Digest; interface TlsHandshakeHash extends Digest { - void init(TlsContext context); - TlsHandshakeHash commit(); + TlsHandshakeHash notifyPRFDetermined(); + + void trackHashAlgorithm(short hashAlgorithm); + + void sealHashAlgorithms(); + + TlsHandshakeHash stopTracking(); + + Digest forkPRFHash(); - TlsHandshakeHash fork(); + byte[] getFinalHash(short hashAlgorithm); } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java index ec11130..20dfef8 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java @@ -1,8 +1,5 @@ package org.bouncycastle.crypto.tls; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.digests.LongDigest; @@ -15,19 +12,19 @@ import org.bouncycastle.util.Arrays; */ 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 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. */ @@ -51,7 +48,7 @@ public class TlsMac this.digestOverhead = 8; } - if (context.getServerVersion().isSSL()) + if (TlsUtils.isSSL(context)) { this.mac = new SSL3Mac(digest); @@ -73,6 +70,12 @@ public class TlsMac } this.mac.init(keyParameter); + + this.macLength = mac.getMacSize(); + if (context.getSecurityParameters().truncatedHMac) + { + this.macLength = Math.min(this.macLength, 10); + } } /** @@ -84,11 +87,11 @@ public class TlsMac } /** - * @return The Keysize of the mac. + * @return The output length of this MAC. */ public int getSize() { - return mac.getMacSize(); + return macLength; } /** @@ -102,42 +105,38 @@ public class TlsMac */ 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(); - ByteArrayOutputStream bosMac = new ByteArrayOutputStream(isSSL ? 11 : 13); - try + byte[] macHeader = new byte[isSSL ? 11 : 13]; + TlsUtils.writeUint64(seqNo, macHeader, 0); + TlsUtils.writeUint8(type, macHeader, 8); + if (!isSSL) { - TlsUtils.writeUint64(seqNo, bosMac); - TlsUtils.writeUint8(type, bosMac); - - if (!isSSL) - { - TlsUtils.writeVersion(serverVersion, bosMac); - } - - TlsUtils.writeUint16(length, bosMac); - } - catch (IOException e) - { - // This should never happen - throw new IllegalStateException("Internal error during mac calculation"); + TlsUtils.writeVersion(serverVersion, macHeader, 9); } + TlsUtils.writeUint16(length, macHeader, macHeader.length - 2); - byte[] macHeader = bosMac.toByteArray(); mac.update(macHeader, 0, macHeader.length); mac.update(message, offset, length); byte[] result = new byte[mac.getMacSize()]; mac.doFinal(result, 0); - return result; + return truncate(result); } public byte[] calculateMacConstantTime(long seqNo, short type, byte[] message, int offset, int length, - int fullLength, byte[] dummyData) + int fullLength, byte[] dummyData) { - /* * Actual MAC only calculated on 'length' bytes... */ @@ -147,7 +146,7 @@ public class TlsMac * ...but ensure a constant number of complete digest blocks are processed (as many as would * be needed for 'fullLength' bytes of input). */ - int headerLength = context.getServerVersion().isSSL() ? 11 : 13; + 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); @@ -164,9 +163,19 @@ public class TlsMac return result; } - private int getDigestBlockCount(int inputLength) + 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java index d5b2b98..d1f6986 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java @@ -26,7 +26,6 @@ public class TlsNullCipher public TlsNullCipher(TlsContext context, Digest clientWriteDigest, Digest serverWriteDigest) throws IOException { - if ((clientWriteDigest == null) != (serverWriteDigest == null)) { throw new TlsFatalAlert(AlertDescription.internal_error); @@ -38,7 +37,6 @@ public class TlsNullCipher if (clientWriteDigest != null) { - int key_block_size = clientWriteDigest.getDigestSize() + serverWriteDigest.getDigestSize(); byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size); @@ -84,7 +82,6 @@ public class TlsNullCipher 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); @@ -100,7 +97,6 @@ public class TlsNullCipher 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); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java index cfabb76..7217bac 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java @@ -4,7 +4,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.math.BigInteger; import java.util.Vector; import org.bouncycastle.asn1.x509.KeyUsage; @@ -22,33 +21,42 @@ import org.bouncycastle.crypto.util.PublicKeyFactory; 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 DHPublicKeyParameters dhAgreeServerPublicKey = null; - protected DHPrivateKeyParameters dhAgreeClientPrivateKey = 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) + 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: - case KeyExchangeAlgorithm.DHE_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() @@ -60,10 +68,61 @@ public class TlsPSKKeyExchange } } - public void processServerCertificate(Certificate serverCertificate) + 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); @@ -100,27 +159,30 @@ public class TlsPSKKeyExchange public boolean requiresServerKeyExchange() { - return keyExchange == KeyExchangeAlgorithm.DHE_PSK; + 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) { - byte[] pBytes = TlsUtils.readOpaque16(input); - byte[] gBytes = TlsUtils.readOpaque16(input); - byte[] YsBytes = TlsUtils.readOpaque16(input); + ServerDHParams serverDHParams = ServerDHParams.parse(input); - BigInteger p = new BigInteger(1, pBytes); - BigInteger g = new BigInteger(1, gBytes); - BigInteger Ys = new BigInteger(1, YsBytes); - - this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Ys, - new DHParameters(p, g))); + this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(serverDHParams.getPublicKey()); + } + else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK) + { + // TODO[RFC 5489] } } @@ -139,7 +201,6 @@ public class TlsPSKKeyExchange public void generateClientKeyExchange(OutputStream output) throws IOException { - if (psk_identity_hint == null) { pskIdentity.skipIdentityHint(); @@ -153,22 +214,26 @@ public class TlsPSKKeyExchange TlsUtils.writeOpaque16(psk_identity, output); - if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) + if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) { - this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, - output); + 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.DHE_PSK) + else if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK) { - this.dhAgreeClientPrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(), - dhAgreeServerPublicKey.getParameters(), output); + this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, + output); } } public byte[] generatePremasterSecret() throws IOException { - byte[] psk = pskIdentity.getPSK(); byte[] other_secret = generateOtherSecret(psk.length); @@ -178,12 +243,22 @@ public class TlsPSKKeyExchange return buf.toByteArray(); } - protected byte[] generateOtherSecret(int pskLength) + protected byte[] generateOtherSecret(int pskLength) throws IOException { - if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK) { - return TlsDHUtils.calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey); + 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) diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java index e408002..88780ea 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java @@ -1,7 +1,14 @@ package org.bouncycastle.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. @@ -20,4 +27,9 @@ public interface TlsPeer * @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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java index 6d8e3d3..2c3b094 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java @@ -2,6 +2,7 @@ package org.bouncycastle.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; @@ -10,6 +11,7 @@ import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; @@ -18,7 +20,6 @@ import org.bouncycastle.util.Integers; */ 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); @@ -32,25 +33,24 @@ public abstract class TlsProtocol 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_SERVER_KEY_EXCHANGE = 5; - protected static final short CS_CERTIFICATE_REQUEST = 6; - protected static final short CS_SERVER_HELLO_DONE = 7; - protected static final short CS_CLIENT_SUPPLEMENTAL_DATA = 8; - protected static final short CS_CLIENT_CERTIFICATE = 9; - protected static final short CS_CLIENT_KEY_EXCHANGE = 10; - protected static final short CS_CERTIFICATE_VERIFY = 11; - protected static final short CS_CLIENT_CHANGE_CIPHER_SPEC = 12; + 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_CHANGE_CIPHER_SPEC = 15; - protected static final short CS_SERVER_FINISHED = 16; + 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 changeCipherSpecQueue = new ByteQueue(); - private ByteQueue alertQueue = new ByteQueue(); + private ByteQueue alertQueue = new ByteQueue(2); private ByteQueue handshakeQueue = new ByteQueue(); /* @@ -65,13 +65,24 @@ public abstract class TlsProtocol private volatile boolean closed = false; private volatile boolean failedWithError = false; private volatile boolean appDataReady = false; - private volatile boolean writeExtraEmptyRecords = true; + 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) @@ -84,8 +95,9 @@ public abstract class TlsProtocol protected abstract TlsPeer getPeer(); - protected abstract void handleChangeCipherSpecMessage() - throws IOException; + protected void handleChangeCipherSpecMessage() throws IOException + { + } protected abstract void handleHandshakeMessage(short type, byte[] buf) throws IOException; @@ -93,37 +105,88 @@ public abstract class TlsProtocol 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? + } - this.expected_verify_data = null; + safeReadRecord(); + } - /* - * We will now read data, until we have completed the handshake. - */ - while (this.connection_state != CS_SERVER_FINISHED) - { - safeReadRecord(); - } + this.recordStream.finaliseHandshake(); - this.recordStream.finaliseHandshake(); + this.splitApplicationDataRecords = !TlsUtils.isTLSv11(getContext()); - ProtocolVersion version = getContext().getServerVersion(); - this.writeExtraEmptyRecords = version.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10); + /* + * If this was an initial handshake, we are now ready to send and receive application data. + */ + if (!appDataReady) + { + this.appDataReady = true; - /* - * 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); + } - 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(); } } @@ -135,26 +198,37 @@ public abstract class TlsProtocol */ switch (protocol) { - case ContentType.change_cipher_spec: - changeCipherSpecQueue.addData(buf, offset, len); - processChangeCipherSpec(); - break; case ContentType.alert: + { alertQueue.addData(buf, offset, len); processAlert(); break; - case ContentType.handshake: - handshakeQueue.addData(buf, offset, len); - processHandshake(); - break; + } case ContentType.application_data: + { if (!appDataReady) { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + 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. @@ -190,9 +264,7 @@ public abstract class TlsProtocol /* * Read the message. */ - byte[] buf = new byte[len]; - handshakeQueue.read(buf, 0, len, 4); - handshakeQueue.removeData(len + 4); + byte[] buf = handshakeQueue.removeData(len, 4); /* * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages @@ -205,7 +277,6 @@ public abstract class TlsProtocol break; case HandshakeType.finished: { - if (this.expected_verify_data == null) { this.expected_verify_data = createVerifyData(!getContext().isServer()); @@ -247,9 +318,7 @@ public abstract class TlsProtocol /* * An alert is always 2 bytes. Read the alert. */ - byte[] tmp = new byte[2]; - alertQueue.read(tmp, 0, 2, 0); - alertQueue.removeData(2); + byte[] tmp = alertQueue.removeData(2, 0); short level = tmp[0]; short description = tmp[1]; @@ -257,20 +326,17 @@ public abstract class TlsProtocol 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; - /* - * Now try to close the stream, ignore errors. - */ - try - { - recordStream.close(); - } - catch (Exception e) - { - } + recordStream.safeClose(); + throw new IOException(TLS_ERROR_MESSAGE); } else @@ -300,27 +366,27 @@ public abstract class TlsProtocol * @throws IOException If the message has an invalid content or the handshake is not in the correct * state. */ - private void processChangeCipherSpec() + private void processChangeCipherSpec(byte[] buf, int off, int len) throws IOException { - while (changeCipherSpecQueue.size() > 0) + for (int i = 0; i < len; ++i) { - /* - * A change cipher spec message is only one byte with the value 1. - */ - byte[] b = new byte[1]; - changeCipherSpecQueue.read(b, 0, 1, 0); - changeCipherSpecQueue.removeData(1); - if (b[0] != 1) + short message = TlsUtils.readUint8(buf, off + i); + + if (message != ChangeCipherSpec.change_cipher_spec) { - /* - * This should never happen. - */ - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.decode_error); } - recordStream.receivedReadCipherSpec(); + if (this.receivedChangeCipherSpec) + { + throw new TlsFatalAlert(AlertDescription.unexpected_message); + } + this.receivedChangeCipherSpec = true; + + recordStream.receivedReadCipherSpec(); + handleChangeCipherSpecMessage(); } } @@ -338,7 +404,6 @@ public abstract class TlsProtocol protected int readApplicationData(byte[] buf, int offset, int len) throws IOException { - if (len < 1) { return 0; @@ -367,9 +432,9 @@ public abstract class TlsProtocol safeReadRecord(); } + len = Math.min(len, applicationDataQueue.size()); - applicationDataQueue.read(buf, offset, len, 0); - applicationDataQueue.removeData(len); + applicationDataQueue.removeData(buf, offset, len, 0); return len; } @@ -378,13 +443,18 @@ public abstract class TlsProtocol { try { - recordStream.readRecord(); + 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()); + this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to read record", e); } throw e; } @@ -392,7 +462,7 @@ public abstract class TlsProtocol { if (!this.closed) { - this.failWithError(AlertLevel.fatal, AlertDescription.internal_error); + this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e); } throw e; } @@ -400,7 +470,7 @@ public abstract class TlsProtocol { if (!this.closed) { - this.failWithError(AlertLevel.fatal, AlertDescription.internal_error); + this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e); } throw e; } @@ -417,7 +487,7 @@ public abstract class TlsProtocol { if (!this.closed) { - this.failWithError(AlertLevel.fatal, e.getAlertDescription()); + this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to write record", e); } throw e; } @@ -425,7 +495,7 @@ public abstract class TlsProtocol { if (!closed) { - this.failWithError(AlertLevel.fatal, AlertDescription.internal_error); + this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e); } throw e; } @@ -433,7 +503,7 @@ public abstract class TlsProtocol { if (!closed) { - this.failWithError(AlertLevel.fatal, AlertDescription.internal_error); + this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e); } throw e; } @@ -467,25 +537,41 @@ public abstract class TlsProtocol /* * 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.writeExtraEmptyRecords) + + if (this.splitApplicationDataRecords) { /* * Protect against known IV attack! * - * DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE. + * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE. */ - safeWriteRecord(ContentType.application_data, TlsUtils.EMPTY_BYTES, 0, 0); + safeWriteRecord(ContentType.application_data, buf, offset, 1); + ++offset; + --len; } - /* - * We are only allowed to write fragments up to 2^14 bytes. - */ - int toWrite = Math.min(len, 1 << 14); - - safeWriteRecord(ContentType.application_data, buf, offset, toWrite); + 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; + } + } + } - offset += 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; } } @@ -507,15 +593,16 @@ public abstract class TlsProtocol } /** - * Terminate this connection with an alert. - * <p/> - * Can be used for normal closure too. - * - * @param alertLevel The level of the alert, an be AlertLevel.fatal or AL_warning. - * @param alertDescription The exact alert message. - * @throws IOException If alert was fatal. + * 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) + protected void failWithError(short alertLevel, short alertDescription, String message, Exception cause) throws IOException { /* @@ -531,27 +618,43 @@ public abstract class TlsProtocol if (alertLevel == AlertLevel.fatal) { /* - * This is a fatal message. + * 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, null, null); - recordStream.close(); - if (alertLevel == AlertLevel.fatal) + raiseAlert(alertLevel, alertDescription, message, cause); + recordStream.safeClose(); + if (alertLevel != AlertLevel.fatal) { - throw new IOException(TLS_ERROR_MESSAGE); + return; } } - else + + throw new IOException(TLS_ERROR_MESSAGE); + } + + protected void invalidateSession() + { + if (this.sessionParameters != null) { - throw new IOException(TLS_ERROR_MESSAGE); + 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); @@ -564,14 +667,13 @@ public abstract class TlsProtocol /* * Wrong checksum in the finished message. */ - this.failWithError(AlertLevel.fatal, AlertDescription.decrypt_error); + 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]; @@ -590,7 +692,6 @@ public abstract class TlsProtocol protected void sendCertificateMessage(Certificate certificate) throws IOException { - if (certificate == null) { certificate = Certificate.EMPTY_CHAIN; @@ -611,25 +712,17 @@ public abstract class TlsProtocol } } - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - TlsUtils.writeUint8(HandshakeType.certificate, bos); - - // Reserve space for length - TlsUtils.writeUint24(0, bos); - - certificate.encode(bos); - byte[] message = bos.toByteArray(); + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate); - // Patch actual length back in - TlsUtils.writeUint24(message.length - 4, message, 1); + certificate.encode(message); - safeWriteRecord(ContentType.handshake, message, 0, message.length); + message.writeToRecordStream(); } protected void sendChangeCipherSpecMessage() throws IOException { - byte[] message = new byte[]{1}; + byte[] message = new byte[]{ 1 }; safeWriteRecord(ContentType.change_cipher_spec, message, 0, message.length); recordStream.sentWriteCipherSpec(); } @@ -639,33 +732,21 @@ public abstract class TlsProtocol { byte[] verify_data = createVerifyData(getContext().isServer()); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - TlsUtils.writeUint8(HandshakeType.finished, bos); - TlsUtils.writeUint24(verify_data.length, bos); - bos.write(verify_data); - byte[] message = bos.toByteArray(); + HandshakeMessage message = new HandshakeMessage(HandshakeType.finished, verify_data.length); - safeWriteRecord(ContentType.handshake, message, 0, message.length); + message.write(verify_data); + + message.writeToRecordStream(); } protected void sendSupplementalDataMessage(Vector supplementalData) throws IOException { + HandshakeMessage message = new HandshakeMessage(HandshakeType.supplemental_data); - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeUint8(HandshakeType.supplemental_data, buf); - - // Reserve space for length - TlsUtils.writeUint24(0, buf); + writeSupplementalData(message, supplementalData); - writeSupplementalData(buf, supplementalData); - - byte[] message = buf.toByteArray(); - - // Patch actual length back in - TlsUtils.writeUint24(message.length - 4, message, 1); - - safeWriteRecord(ContentType.handshake, message, 0, message.length); + message.writeToRecordStream(); } protected byte[] createVerifyData(boolean isServer) @@ -674,12 +755,12 @@ public abstract class TlsProtocol if (isServer) { - return TlsUtils.calculateVerifyData(context, "server finished", - recordStream.getCurrentHash(TlsUtils.SSL_SERVER)); + return TlsUtils.calculateVerifyData(context, ExporterLabel.server_finished, + getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_SERVER)); } - return TlsUtils.calculateVerifyData(context, "client finished", - recordStream.getCurrentHash(TlsUtils.SSL_CLIENT)); + return TlsUtils.calculateVerifyData(context, ExporterLabel.client_finished, + getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_CLIENT)); } /** @@ -702,7 +783,7 @@ public abstract class TlsProtocol { raiseWarning(AlertDescription.user_canceled, "User canceled handshake"); } - this.failWithError(AlertLevel.warning, AlertDescription.close_notify); + this.failWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null); } } @@ -712,28 +793,18 @@ public abstract class TlsProtocol recordStream.flush(); } - protected static boolean arrayContains(short[] a, short n) - { - for (int i = 0; i < a.length; ++i) - { - if (a[i] == n) - { - return true; - } - } - return false; - } - - protected static boolean arrayContains(int[] a, int n) + protected short processMaxFragmentLengthExtension(Hashtable clientExtensions, Hashtable serverExtensions, short alertDescription) + throws IOException { - for (int i = 0; i < a.length; ++i) + short maxFragmentLength = TlsExtensionsUtils.getMaxFragmentLengthExtension(serverExtensions); + if (maxFragmentLength >= 0 && !this.resumedSession) { - if (a[i] == n) + if (maxFragmentLength != TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions)) { - return true; + throw new TlsFatalAlert(alertDescription); } } - return false; + return maxFragmentLength; } /** @@ -753,25 +824,28 @@ public abstract class TlsProtocol protected static byte[] createRandomBlock(SecureRandom random) { + random.setSeed(System.currentTimeMillis()); + byte[] result = new byte[32]; random.nextBytes(result); - TlsUtils.writeGMTUnixTime(result, 0); + /* + * 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 { - - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeOpaque8(renegotiated_connection, buf); - return buf.toByteArray(); + return TlsUtils.encodeOpaque8(renegotiated_connection); } protected static void establishMasterSecret(TlsContext context, TlsKeyExchange keyExchange) throws IOException { - byte[] pre_master_secret = keyExchange.generatePremasterSecret(); try @@ -792,10 +866,26 @@ public abstract class TlsProtocol } } + /** + * '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; @@ -812,13 +902,13 @@ public abstract class TlsProtocol while (buf.available() > 0) { - Integer extType = Integers.valueOf(TlsUtils.readUint16(buf)); - byte[] extValue = TlsUtils.readOpaque16(buf); + 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(extType, extValue)) + if (null != extensions.put(extension_type, extension_data)) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } @@ -830,7 +920,6 @@ public abstract class TlsProtocol protected static Vector readSupplementalDataMessage(ByteArrayInputStream input) throws IOException { - byte[] supp_data = TlsUtils.readOpaque24(input); assertEmpty(input); @@ -853,17 +942,18 @@ public abstract class TlsProtocol protected static void writeExtensions(OutputStream output, Hashtable extensions) throws IOException { - ByteArrayOutputStream buf = new ByteArrayOutputStream(); Enumeration keys = extensions.keys(); while (keys.hasMoreElements()) { - Integer extType = (Integer)keys.nextElement(); - byte[] extValue = (byte[])extensions.get(extType); + Integer key = (Integer)keys.nextElement(); + int extension_type = key.intValue(); + byte[] extension_data = (byte[])extensions.get(key); - TlsUtils.writeUint16(extType.intValue(), buf); - TlsUtils.writeOpaque16(extValue, buf); + TlsUtils.checkUint16(extension_type); + TlsUtils.writeUint16(extension_type, buf); + TlsUtils.writeOpaque16(extension_data, buf); } byte[] extBytes = buf.toByteArray(); @@ -874,14 +964,15 @@ public abstract class TlsProtocol 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); - TlsUtils.writeUint16(entry.getDataType(), buf); + int supp_data_type = entry.getDataType(); + TlsUtils.checkUint16(supp_data_type); + TlsUtils.writeUint16(supp_data_type, buf); TlsUtils.writeOpaque16(entry.getData(), buf); } @@ -890,54 +981,137 @@ public abstract class TlsProtocol TlsUtils.writeOpaque24(supp_data, output); } - protected static int getPRFAlgorithm(int ciphersuite) + 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_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: 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_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: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM: + case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8: case CipherSuite.TLS_RSA_WITH_NULL_SHA256: - return PRFAlgorithm.tls_prf_sha256; + { + if (isTLSv12) + { + return PRFAlgorithm.tls_prf_sha256; + } + throw new TlsFatalAlert(AlertDescription.illegal_parameter); + } - 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: 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: - return PRFAlgorithm.tls_prf_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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java index 24eec53..8970968 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java @@ -40,7 +40,6 @@ public class TlsRSAKeyExchange public void processServerCredentials(TlsCredentials serverCredentials) throws IOException { - if (!(serverCredentials instanceof TlsEncryptionCredentials)) { throw new TlsFatalAlert(AlertDescription.internal_error); @@ -54,7 +53,6 @@ public class TlsRSAKeyExchange public void processServerCertificate(Certificate serverCertificate) throws IOException { - if (serverCertificate.isEmpty()) { throw new TlsFatalAlert(AlertDescription.bad_certificate); @@ -115,15 +113,14 @@ public class TlsRSAKeyExchange public void generateClientKeyExchange(OutputStream output) throws IOException { - this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, output); + this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, rsaServerPublicKey, output); } public void processClientKeyExchange(InputStream input) throws IOException { - byte[] encryptedPreMasterSecret; - if (context.getServerVersion().isSSL()) + if (TlsUtils.isSSL(context)) { // TODO Do any SSLv3 clients actually include the length? encryptedPreMasterSecret = Streams.readAll(input); @@ -133,68 +130,7 @@ public class TlsRSAKeyExchange encryptedPreMasterSecret = TlsUtils.readOpaque16(input); } - ProtocolVersion clientVersion = context.getClientVersion(); - - /* - * RFC 5246 7.4.7.1. - */ - { - // 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]; - this.context.getSecureRandom().nextBytes(R); - - byte[] M = TlsUtils.EMPTY_BYTES; - try - { - M = serverCredentials.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); - this.premasterSecret = R; - } - else - { - /* - * 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); - } - this.premasterSecret = M; - } - } + this.premasterSecret = TlsRSAUtils.safeDecryptPreMasterSecret(context, serverCredentials, encryptedPreMasterSecret); } public byte[] generatePremasterSecret() diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java index d9f7975..35538a7 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java @@ -5,6 +5,7 @@ import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.digests.NullDigest; import org.bouncycastle.crypto.encodings.PKCS1Encoding; import org.bouncycastle.crypto.engines.RSABlindedEngine; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; @@ -12,40 +13,37 @@ import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.params.RSAKeyParameters; import org.bouncycastle.crypto.signers.GenericSigner; import org.bouncycastle.crypto.signers.RSADigestSigner; -import org.bouncycastle.util.Arrays; public class TlsRSASigner extends AbstractTlsSigner { - - public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1) + public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, + AsymmetricKeyParameter privateKey, byte[] hash) throws CryptoException { - - AsymmetricBlockCipher engine = createRSAImpl(); - engine.init(true, new ParametersWithRandom(privateKey, this.context.getSecureRandom())); - return engine.processBlock(md5AndSha1, 0, md5AndSha1.length); + Signer signer = makeSigner(algorithm, true, true, + new ParametersWithRandom(privateKey, this.context.getSecureRandom())); + signer.update(hash, 0, hash.length); + return signer.generateSignature(); } - public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1) + public boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes, + AsymmetricKeyParameter publicKey, byte[] hash) throws CryptoException { - - AsymmetricBlockCipher engine = createRSAImpl(); - engine.init(false, publicKey); - byte[] signed = engine.processBlock(sigBytes, 0, sigBytes.length); - return Arrays.constantTimeAreEqual(signed, md5AndSha1); + Signer signer = makeSigner(algorithm, true, false, publicKey); + signer.update(hash, 0, hash.length); + return signer.verifySignature(sigBytes); } - public Signer createSigner(AsymmetricKeyParameter privateKey) + public Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey) { - return makeSigner(new CombinedHash(), true, - new ParametersWithRandom(privateKey, this.context.getSecureRandom())); + return makeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom())); } - public Signer createVerifyer(AsymmetricKeyParameter publicKey) + public Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey) { - return makeSigner(new CombinedHash(), false, publicKey); + return makeSigner(algorithm, false, false, publicKey); } public boolean isValidPublicKey(AsymmetricKeyParameter publicKey) @@ -53,16 +51,41 @@ public class TlsRSASigner return publicKey instanceof RSAKeyParameters && !publicKey.isPrivate(); } - protected Signer makeSigner(Digest d, boolean forSigning, CipherParameters cp) + 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 (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion())) + 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); + s = new RSADigestSigner(d, TlsUtils.getOIDForHashAlgorithm(algorithm.getHash())); } else { diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java index f67e572..e3856bd 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java @@ -12,8 +12,7 @@ import org.bouncycastle.crypto.params.RSAKeyParameters; public class TlsRSAUtils { public static byte[] generateEncryptedPreMasterSecret(TlsContext context, RSAKeyParameters rsaServerPublicKey, - OutputStream output) - throws IOException + OutputStream output) throws IOException { /* * Choose a PremasterSecret and send it encrypted to the server @@ -29,7 +28,7 @@ public class TlsRSAUtils { byte[] encryptedPreMasterSecret = encoding.processBlock(premasterSecret, 0, premasterSecret.length); - if (context.getServerVersion().isSSL()) + if (TlsUtils.isSSL(context)) { // TODO Do any SSLv3 servers actually expect the length? output.write(encryptedPreMasterSecret); @@ -49,4 +48,69 @@ public class TlsRSAUtils 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java index b928b91..452fbf9 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java @@ -13,18 +13,16 @@ import org.bouncycastle.crypto.Signer; import org.bouncycastle.crypto.agreement.srp.SRP6Client; import org.bouncycastle.crypto.agreement.srp.SRP6Util; import org.bouncycastle.crypto.digests.SHA1Digest; -import org.bouncycastle.crypto.io.SignerInputStream; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.io.TeeInputStream; /** * TLS 1.1 SRP key exchange (RFC 5054). */ -public class TlsSRPKeyExchange - extends AbstractTlsKeyExchange +public class TlsSRPKeyExchange extends AbstractTlsKeyExchange { - protected TlsSigner tlsSigner; protected byte[] identity; protected byte[] password; @@ -37,7 +35,6 @@ public class TlsSRPKeyExchange public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity, byte[] password) { - super(keyExchange, supportedSignatureAlgorithms); switch (keyExchange) @@ -64,14 +61,12 @@ public class TlsSRPKeyExchange { super.init(context); - if (this.tlsSigner != null) - { + if (this.tlsSigner != null) { this.tlsSigner.init(context); } } - public void skipServerCredentials() - throws IOException + public void skipServerCredentials() throws IOException { if (tlsSigner != null) { @@ -79,10 +74,8 @@ public class TlsSRPKeyExchange } } - public void processServerCertificate(Certificate serverCertificate) - throws IOException + public void processServerCertificate(Certificate serverCertificate) throws IOException { - if (tlsSigner == null) { throw new TlsFatalAlert(AlertDescription.unexpected_message); @@ -119,31 +112,31 @@ public class TlsSRPKeyExchange return true; } - public void processServerKeyExchange(InputStream input) - throws IOException + public void processServerKeyExchange(InputStream input) throws IOException { - SecurityParameters securityParameters = context.getSecurityParameters(); - InputStream sigIn = input; - Signer signer = null; + SignerInputBuffer buf = null; + InputStream teeIn = input; if (tlsSigner != null) { - signer = initVerifyer(tlsSigner, securityParameters); - sigIn = new SignerInputStream(input, signer); + buf = new SignerInputBuffer(); + teeIn = new TeeInputStream(input, buf); } - byte[] NBytes = TlsUtils.readOpaque16(sigIn); - byte[] gBytes = TlsUtils.readOpaque16(sigIn); - byte[] sBytes = TlsUtils.readOpaque8(sigIn); - byte[] BBytes = TlsUtils.readOpaque16(sigIn); + byte[] NBytes = TlsUtils.readOpaque16(teeIn); + byte[] gBytes = TlsUtils.readOpaque16(teeIn); + byte[] sBytes = TlsUtils.readOpaque8(teeIn); + byte[] BBytes = TlsUtils.readOpaque16(teeIn); - if (signer != null) + if (buf != null) { - byte[] sigByte = TlsUtils.readOpaque16(input); + DigitallySigned signed_params = DigitallySigned.parse(context, input); - if (!signer.verifySignature(sigByte)) + Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters); + buf.updateSigner(signer); + if (!signer.verifySignature(signed_params.getSignature())) { throw new TlsFatalAlert(AlertDescription.decrypt_error); } @@ -153,7 +146,7 @@ public class TlsSRPKeyExchange BigInteger g = new BigInteger(1, gBytes); // TODO Validate group parameters (see RFC 5054) - // handler.failWithError(AlertLevel.fatal, AlertDescription.insufficient_security); +// throw new TlsFatalAlert(AlertDescription.insufficient_security); this.s = sBytes; @@ -173,28 +166,23 @@ public class TlsSRPKeyExchange this.srpClient.init(N, g, new SHA1Digest(), context.getSecureRandom()); } - public void validateCertificateRequest(CertificateRequest certificateRequest) - throws IOException + public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - public void processClientCredentials(TlsCredentials clientCredentials) - throws IOException + public void processClientCredentials(TlsCredentials clientCredentials) throws IOException { throw new TlsFatalAlert(AlertDescription.internal_error); } - public void generateClientKeyExchange(OutputStream output) - throws IOException + public void generateClientKeyExchange(OutputStream output) throws IOException { - byte[] keData = BigIntegers.asUnsignedByteArray(srpClient.generateClientCredentials(s, this.identity, - this.password)); - TlsUtils.writeOpaque16(keData, output); + BigInteger A = srpClient.generateClientCredentials(s, this.identity, this.password); + TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(A), output); } - public byte[] generatePremasterSecret() - throws IOException + public byte[] generatePremasterSecret() throws IOException { try { @@ -207,9 +195,9 @@ public class TlsSRPKeyExchange } } - protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters) + protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters) { - Signer signer = tlsSigner.createVerifyer(this.serverPublicKey); + 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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java new file mode 100644 index 0000000..7fe5fb8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java @@ -0,0 +1,49 @@ +package org.bouncycastle.crypto.tls; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Hashtable; + +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java index f82f94d..da98b7a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java @@ -12,36 +12,24 @@ import org.bouncycastle.util.Integers; */ 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 { - - if (extensions == null) - { - return null; - } - byte[] extensionValue = (byte[])extensions.get(EXT_use_srtp); - if (extensionValue == null) - { - return null; - } - return readUseSRTPExtension(extensionValue); + 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"); @@ -50,9 +38,7 @@ public class TlsSRTPUtils ByteArrayOutputStream buf = new ByteArrayOutputStream(); // SRTPProtectionProfiles - int[] protectionProfiles = useSRTPData.getProtectionProfiles(); - TlsUtils.writeUint16(2 * protectionProfiles.length, buf); - TlsUtils.writeUint16Array(protectionProfiles, buf); + TlsUtils.writeUint16ArrayWithUint16Length(useSRTPData.getProtectionProfiles(), buf); // srtp_mki TlsUtils.writeOpaque8(useSRTPData.getMki(), buf); @@ -60,16 +46,15 @@ public class TlsSRTPUtils return buf.toByteArray(); } - public static UseSRTPData readUseSRTPExtension(byte[] extensionValue) + public static UseSRTPData readUseSRTPExtension(byte[] extensionData) throws IOException { - - if (extensionValue == null) + if (extensionData == null) { - throw new IllegalArgumentException("'extensionValue' cannot be null"); + throw new IllegalArgumentException("'extensionData' cannot be null"); } - ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue); + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); // SRTPProtectionProfiles int length = TlsUtils.readUint16(buf); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java index 0b46391..85c0a9a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java @@ -7,11 +7,9 @@ import java.util.Vector; public interface TlsServer extends TlsPeer { - void init(TlsServerContext context); - void notifyClientVersion(ProtocolVersion clientVersion) - throws IOException; + void notifyClientVersion(ProtocolVersion clientVersion) throws IOException; void notifyOfferedCipherSuites(int[] offeredCipherSuites) throws IOException; @@ -19,9 +17,6 @@ public interface TlsServer void notifyOfferedCompressionMethods(short[] offeredCompressionMethods) throws IOException; - void notifySecureRenegotiation(boolean secureNegotiation) - throws IOException; - // Hashtable is (Integer -> byte[]) void processClientExtensions(Hashtable clientExtensions) throws IOException; @@ -46,32 +41,41 @@ public interface TlsServer 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 <i>RFC 3546 + * 3.6. Certificate Status Request</i>. 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(); + 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 a Certificate - * {@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). + * 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; - TlsCompression getCompression() - throws IOException; - - TlsCipher getCipher() - throws IOException; - /** * RFC 5077 3.3. NewSessionTicket Handshake Message. * <p/> @@ -83,7 +87,4 @@ public interface TlsServer */ NewSessionTicket getNewSessionTicket() throws IOException; - - void notifyHandshakeComplete() - throws IOException; } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java index 2fa4029..48f028a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java @@ -6,7 +6,6 @@ class TlsServerContextImpl extends AbstractTlsContext implements TlsServerContext { - TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters) { super(secureRandom, securityParameters); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java index 961669f..056d22a 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java @@ -1,12 +1,10 @@ package org.bouncycastle.crypto.tls; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.SecureRandom; -import java.util.Hashtable; import java.util.Vector; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; @@ -17,25 +15,15 @@ import org.bouncycastle.util.Arrays; public class TlsServerProtocol extends TlsProtocol { - protected TlsServer tlsServer = null; protected TlsServerContextImpl tlsServerContext = null; - protected int[] offeredCipherSuites; - protected short[] offeredCompressionMethods; - protected Hashtable clientExtensions; - - protected int selectedCipherSuite; - protected short selectedCompressionMethod; - protected Hashtable serverExtensions; - protected TlsKeyExchange keyExchange = null; protected TlsCredentials serverCredentials = null; protected CertificateRequest certificateRequest = null; protected short clientCertificateType = -1; - protected Certificate clientCertificate = null; - protected byte[] certificateVerifyHash = null; + protected TlsHandshakeHash prepareFinishHash = null; public TlsServerProtocol(InputStream input, OutputStream output, SecureRandom secureRandom) { @@ -51,14 +39,13 @@ public class TlsServerProtocol 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"); + throw new IllegalStateException("'accept' can only be called once"); } this.tlsServer = tlsServer; @@ -74,8 +61,16 @@ public class TlsServerProtocol this.recordStream.setRestrictReadVersion(false); completeHandshake(); + } - this.tlsServer.notifyHandshakeComplete(); + protected void cleanupHandshake() + { + super.cleanupHandshake(); + + this.keyExchange = null; + this.serverCredentials = null; + this.certificateRequest = null; + this.prepareFinishHash = null; } protected AbstractTlsContext getContext() @@ -88,36 +83,9 @@ public class TlsServerProtocol return tlsServer; } - protected void handleChangeCipherSpecMessage() - throws IOException - { - - switch (this.connection_state) - { - case CS_CLIENT_KEY_EXCHANGE: - { - if (this.certificateVerifyHash != null) - { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - } - // NB: Fall through to next case label - } - case CS_CERTIFICATE_VERIFY: - { - this.connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC; - break; - } - default: - { - this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); - } - } - } - protected void handleHandshakeMessage(short type, byte[] data) throws IOException { - ByteArrayInputStream buf = new ByteArrayInputStream(data); switch (type) @@ -134,21 +102,6 @@ public class TlsServerProtocol sendServerHelloMessage(); this.connection_state = CS_SERVER_HELLO; - // TODO This block could really be done before actually sending the hello - { - securityParameters.prfAlgorithm = getPRFAlgorithm(selectedCipherSuite); - securityParameters.compressionAlgorithm = this.selectedCompressionMethod; - - /* - * 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; - - recordStream.notifyHelloComplete(); - } - Vector serverSupplementalData = tlsServer.getServerSupplementalData(); if (serverSupplementalData != null) { @@ -160,6 +113,9 @@ public class TlsServerProtocol this.keyExchange.init(getContext()); this.serverCredentials = tlsServer.getCredentials(); + + Certificate serverCertificate = null; + if (this.serverCredentials == null) { this.keyExchange.skipServerCredentials(); @@ -167,10 +123,29 @@ public class TlsServerProtocol else { this.keyExchange.processServerCredentials(this.serverCredentials); - sendCertificateMessage(this.serverCredentials.getCertificate()); + + 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) { @@ -184,7 +159,11 @@ public class TlsServerProtocol if (this.certificateRequest != null) { this.keyExchange.validateCertificateRequest(certificateRequest); + sendCertificateRequestMessage(certificateRequest); + + TlsUtils.trackHashAlgorithms(this.recordStream.getHandshakeHash(), + this.certificateRequest.getSupportedSignatureAlgorithms()); } } this.connection_state = CS_CERTIFICATE_REQUEST; @@ -192,12 +171,12 @@ public class TlsServerProtocol sendServerHelloDoneMessage(); this.connection_state = CS_SERVER_HELLO_DONE; + this.recordStream.getHandshakeHash().sealHashAlgorithms(); + break; } default: - { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - } + throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } @@ -212,9 +191,7 @@ public class TlsServerProtocol break; } default: - { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - } + throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } @@ -231,16 +208,14 @@ public class TlsServerProtocol { if (this.certificateRequest == null) { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } receiveCertificateMessage(buf); this.connection_state = CS_CLIENT_CERTIFICATE; break; } default: - { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - } + throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } @@ -261,10 +236,7 @@ public class TlsServerProtocol } else { - - ProtocolVersion equivalentTLSVersion = getContext().getServerVersion().getEquivalentTLSVersion(); - - if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(equivalentTLSVersion)) + if (TlsUtils.isTLSv12(getContext())) { /* * RFC 5246 If no suitable certificate is available, the client MUST send a @@ -272,13 +244,13 @@ public class TlsServerProtocol * * NOTE: In previous RFCs, this was SHOULD instead of MUST. */ - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } - else if (equivalentTLSVersion.isSSL()) + else if (TlsUtils.isSSL(getContext())) { - if (clientCertificate == null) + if (this.peerCertificate == null) { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } } else @@ -295,9 +267,7 @@ public class TlsServerProtocol break; } default: - { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - } + throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } @@ -312,18 +282,18 @@ public class TlsServerProtocol * signing capability (i.e., all certificates except those containing fixed * Diffie-Hellman parameters). */ - if (this.certificateVerifyHash == null) + if (!expectCertificateVerifyMessage()) { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } + receiveCertificateVerifyMessage(buf); this.connection_state = CS_CERTIFICATE_VERIFY; + break; } default: - { - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - } + throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } @@ -331,24 +301,33 @@ public class TlsServerProtocol { switch (this.connection_state) { - case CS_CLIENT_CHANGE_CIPHER_SPEC: + 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 (expectSessionTicket) + if (this.expectSessionTicket) { sendNewSessionTicketMessage(tlsServer.getNewSessionTicket()); + sendChangeCipherSpecMessage(); } this.connection_state = CS_SERVER_SESSION_TICKET; - sendChangeCipherSpecMessage(); - this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC; - sendFinishedMessage(); this.connection_state = CS_SERVER_FINISHED; + this.connection_state = CS_END; break; + } default: - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); + throw new TlsFatalAlert(AlertDescription.unexpected_message); } break; } @@ -360,9 +339,7 @@ public class TlsServerProtocol case HandshakeType.server_hello_done: case HandshakeType.session_ticket: default: - // We do not support this! - this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); - break; + throw new TlsFatalAlert(AlertDescription.unexpected_message); } } @@ -377,7 +354,7 @@ public class TlsServerProtocol * 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 (getContext().getServerVersion().isSSL() && certificateRequest != null) + if (TlsUtils.isSSL(getContext()) && certificateRequest != null) { notifyClientCertificate(Certificate.EMPTY_CHAIN); } @@ -393,18 +370,17 @@ public class TlsServerProtocol protected void notifyClientCertificate(Certificate clientCertificate) throws IOException { - if (certificateRequest == null) { throw new IllegalStateException(); } - if (this.clientCertificate != null) + if (this.peerCertificate != null) { throw new TlsFatalAlert(AlertDescription.unexpected_message); } - this.clientCertificate = clientCertificate; + this.peerCertificate = clientCertificate; if (clientCertificate.isEmpty()) { @@ -439,7 +415,6 @@ public class TlsServerProtocol protected void receiveCertificateMessage(ByteArrayInputStream buf) throws IOException { - Certificate clientCertificate = Certificate.parse(buf); assertEmpty(buf); @@ -450,22 +425,24 @@ public class TlsServerProtocol protected void receiveCertificateVerifyMessage(ByteArrayInputStream buf) throws IOException { - - byte[] clientCertificateSignature = TlsUtils.readOpaque16(buf); + DigitallySigned clientCertificateVerify = DigitallySigned.parse(getContext(), buf); assertEmpty(buf); // Verify the CertificateVerify message contains a correct signature. try { - TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType); - tlsSigner.init(getContext()); + // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned + byte[] certificateVerifyHash = getCurrentPRFHash(getContext(), prepareFinishHash, null); - org.bouncycastle.asn1.x509.Certificate x509Cert = this.clientCertificate.getCertificateAt(0); + org.bouncycastle.asn1.x509.Certificate x509Cert = this.peerCertificate.getCertificateAt(0); SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo); - tlsSigner.verifyRawSignature(clientCertificateSignature, publicKey, this.certificateVerifyHash); + TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType); + tlsSigner.init(getContext()); + tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(), + clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash); } catch (Exception e) { @@ -476,42 +453,45 @@ public class TlsServerProtocol protected void receiveClientHelloMessage(ByteArrayInputStream buf) throws IOException { - ProtocolVersion client_version = TlsUtils.readVersion(buf); if (client_version.isDTLS()) { - this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - /* - * Read the client random - */ 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) { - this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + 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) { - this.failWithError(AlertLevel.fatal, AlertDescription.decode_error); + throw new TlsFatalAlert(AlertDescription.decode_error); } + this.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf); /* - * 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." + * 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. */ - this.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf); - int compression_methods_length = TlsUtils.readUint8(buf); if (compression_methods_length < 1) { - this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter); } - this.offeredCompressionMethods = TlsUtils.readUint8Array(compression_methods_length, buf); /* @@ -545,7 +525,7 @@ public class TlsServerProtocol * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag * to TRUE. */ - if (arrayContains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) + if (Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)) { this.secure_renegotiation = true; } @@ -554,22 +534,19 @@ public class TlsServerProtocol * The server MUST check if the "renegotiation_info" extension is included in the * ClientHello. */ - if (clientExtensions != null) + byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo); + if (renegExtData != null) { - byte[] renegExtValue = (byte[])clientExtensions.get(EXT_RenegotiationInfo); - if (renegExtValue != 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))) { - /* - * 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(renegExtValue, createRenegotiationInfo(TlsUtils.EMPTY_BYTES))) - { - this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); - } + throw new TlsFatalAlert(AlertDescription.handshake_failure); } } } @@ -585,81 +562,65 @@ public class TlsServerProtocol protected void receiveClientKeyExchangeMessage(ByteArrayInputStream buf) throws IOException { - this.keyExchange.processClientKeyExchange(buf); assertEmpty(buf); establishMasterSecret(getContext(), keyExchange); + recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher()); - /* - * Initialize our cipher suite - */ - recordStream.setPendingConnectionState(tlsServer.getCompression(), tlsServer.getCipher()); + this.prepareFinishHash = recordStream.prepareToFinish(); - if (expectCertificateVerifyMessage()) + if (!expectSessionTicket) { - this.certificateVerifyHash = recordStream.getCurrentHash(null); + sendChangeCipherSpecMessage(); } } protected void sendCertificateRequestMessage(CertificateRequest certificateRequest) throws IOException { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_request); - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeUint8(HandshakeType.certificate_request, buf); + certificateRequest.encode(message); - // Reserve space for length - TlsUtils.writeUint24(0, buf); + message.writeToRecordStream(); + } - certificateRequest.encode(buf); - byte[] message = buf.toByteArray(); + protected void sendCertificateStatusMessage(CertificateStatus certificateStatus) + throws IOException + { + HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_status); - // Patch actual length back in - TlsUtils.writeUint24(message.length - 4, message, 1); + certificateStatus.encode(message); - safeWriteRecord(ContentType.handshake, message, 0, message.length); + message.writeToRecordStream(); } protected void sendNewSessionTicketMessage(NewSessionTicket newSessionTicket) throws IOException { - if (newSessionTicket == null) { throw new TlsFatalAlert(AlertDescription.internal_error); } - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeUint8(HandshakeType.session_ticket, buf); - - // Reserve space for length - TlsUtils.writeUint24(0, buf); + HandshakeMessage message = new HandshakeMessage(HandshakeType.session_ticket); - newSessionTicket.encode(buf); - byte[] message = buf.toByteArray(); + newSessionTicket.encode(message); - // Patch actual length back in - TlsUtils.writeUint24(message.length - 4, message, 1); - - safeWriteRecord(ContentType.handshake, message, 0, message.length); + message.writeToRecordStream(); } protected void sendServerHelloMessage() throws IOException { - - ByteArrayOutputStream buf = new ByteArrayOutputStream(); - TlsUtils.writeUint8(HandshakeType.server_hello, buf); - - // Reserve space for length - TlsUtils.writeUint24(0, buf); + HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello); ProtocolVersion server_version = tlsServer.getServerVersion(); if (!server_version.isEqualOrEarlierVersionOf(getContext().getClientVersion())) { - this.failWithError(AlertLevel.fatal, AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error); } recordStream.setReadVersion(server_version); @@ -667,32 +628,34 @@ public class TlsServerProtocol recordStream.setRestrictReadVersion(true); getContext().setServerVersion(server_version); - TlsUtils.writeVersion(server_version, buf); + TlsUtils.writeVersion(server_version, message); - buf.write(this.securityParameters.serverRandom); + 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, buf); + TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, message); - this.selectedCipherSuite = tlsServer.getSelectedCipherSuite(); - if (!arrayContains(this.offeredCipherSuites, this.selectedCipherSuite) - || this.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL - || this.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + int selectedCipherSuite = tlsServer.getSelectedCipherSuite(); + if (!Arrays.contains(this.offeredCipherSuites, selectedCipherSuite) + || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL + || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) { - this.failWithError(AlertLevel.fatal, AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error); } + securityParameters.cipherSuite = selectedCipherSuite; - this.selectedCompressionMethod = tlsServer.getSelectedCompressionMethod(); - if (!arrayContains(this.offeredCompressionMethods, this.selectedCompressionMethod)) + short selectedCompressionMethod = tlsServer.getSelectedCompressionMethod(); + if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod)) { - this.failWithError(AlertLevel.fatal, AlertDescription.internal_error); + throw new TlsFatalAlert(AlertDescription.internal_error); } + securityParameters.compressionAlgorithm = selectedCompressionMethod; - TlsUtils.writeUint16(this.selectedCipherSuite, buf); - TlsUtils.writeUint8(this.selectedCompressionMethod, buf); + TlsUtils.writeUint16(selectedCipherSuite, message); + TlsUtils.writeUint8(selectedCompressionMethod, message); this.serverExtensions = tlsServer.getServerExtensions(); @@ -701,9 +664,8 @@ public class TlsServerProtocol */ if (this.secure_renegotiation) { - - boolean noRenegExt = this.serverExtensions == null - || !this.serverExtensions.containsKey(EXT_RenegotiationInfo); + byte[] renegExtData = TlsUtils.getExtensionData(this.serverExtensions, EXT_RenegotiationInfo); + boolean noRenegExt = (null == renegExtData); if (noRenegExt) { @@ -714,55 +676,81 @@ public class TlsServerProtocol * because the client is signaling its willingness to receive the extension via the * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. */ - if (this.serverExtensions == null) - { - this.serverExtensions = new Hashtable(); - } /* * 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.expectSessionTicket = serverExtensions.containsKey(EXT_SessionTicket); - writeExtensions(buf, this.serverExtensions); + 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); } - byte[] message = buf.toByteArray(); + securityParameters.prfAlgorithm = getPRFAlgorithm(getContext(), securityParameters.getCipherSuite()); - // Patch actual length back in - TlsUtils.writeUint24(message.length - 4, message, 1); + /* + * 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; - safeWriteRecord(ContentType.handshake, message, 0, message.length); + 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); - safeWriteRecord(ContentType.handshake, message, 0, message.length); + writeHandshakeMessage(message, 0, message.length); } protected void sendServerKeyExchangeMessage(byte[] serverKeyExchange) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); + HandshakeMessage message = new HandshakeMessage(HandshakeType.server_key_exchange, serverKeyExchange.length); - TlsUtils.writeUint8(HandshakeType.server_key_exchange, bos); - TlsUtils.writeUint24(serverKeyExchange.length, bos); - bos.write(serverKeyExchange); - byte[] message = bos.toByteArray(); + message.write(serverKeyExchange); - safeWriteRecord(ContentType.handshake, message, 0, message.length); + message.writeToRecordStream(); } protected boolean expectCertificateVerifyMessage() diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java new file mode 100644 index 0000000..9b5ad46 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java @@ -0,0 +1,12 @@ +package org.bouncycastle.crypto.tls; + +public interface TlsSession +{ + SessionParameters exportSessionParameters(); + + byte[] getSessionID(); + + void invalidate(); + + boolean isResumable(); +} diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java new file mode 100644 index 0000000..615c442 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java @@ -0,0 +1,48 @@ +package org.bouncycastle.crypto.tls; + +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java index 2b61507..68826d2 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java @@ -6,18 +6,29 @@ import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java index 7067fa2..39ee99c 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java @@ -5,6 +5,8 @@ import java.io.IOException; public interface TlsSignerCredentials extends TlsCredentials { - byte[] generateCertificateSignature(byte[] md5andsha1) + byte[] generateCertificateSignature(byte[] hash) throws IOException; + + SignatureAndHashAlgorithm getSignatureAndHashAlgorithm(); } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java index 1755c2d..178731d 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java @@ -11,6 +11,8 @@ import org.bouncycastle.util.Arrays; public class TlsStreamCipher implements TlsCipher { + private static boolean encryptThenMAC = false; + protected TlsContext context; protected StreamCipher encryptCipher; @@ -20,11 +22,9 @@ public class TlsStreamCipher protected TlsMac readMac; public TlsStreamCipher(TlsContext context, StreamCipher clientWriteCipher, - StreamCipher serverWriteCipher, Digest clientWriteDigest, Digest serverWriteDigest, - int cipherKeySize) - throws IOException + StreamCipher serverWriteCipher, Digest clientWriteDigest, Digest serverWriteDigest, + int cipherKeySize) throws IOException { - boolean isServer = context.isServer(); this.context = context; @@ -89,38 +89,75 @@ public class TlsStreamCipher public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len) { - byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, 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 + mac.length]; + byte[] outBuf = new byte[len + writeMac.getSize()]; - encryptCipher.processBytes(plaintext, offset, len, outbuf, 0); - encryptCipher.processBytes(mac, 0, mac.length, outbuf, len); + 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; + 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); } - byte[] deciphered = new byte[len]; - decryptCipher.processBytes(ciphertext, offset, len, deciphered, 0); + int plaintextLength = len - macSize; - int macInputLen = 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); + } + } - byte[] receivedMac = Arrays.copyOfRange(deciphered, macInputLen, len); - byte[] computedMac = readMac.calculateMac(seqNo, type, deciphered, 0, macInputLen); + 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); } - - return Arrays.copyOfRange(deciphered, 0, macInputLen); } } diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java index 8b16210..dae9ff5 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java @@ -9,7 +9,10 @@ import java.io.OutputStream; import java.util.Hashtable; import java.util.Vector; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.Extensions; @@ -44,11 +47,72 @@ public class TlsUtils 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; @@ -74,17 +138,43 @@ public class TlsUtils 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 { @@ -130,6 +220,17 @@ public class TlsUtils 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); @@ -143,14 +244,14 @@ public class TlsUtils public static void writeUint64(long i, OutputStream output) throws IOException { - output.write((int)(i >> 56)); - output.write((int)(i >> 48)); - output.write((int)(i >> 40)); - output.write((int)(i >> 32)); - output.write((int)(i >> 24)); - output.write((int)(i >> 16)); - output.write((int)(i >> 8)); - output.write((int)(i)); + 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) @@ -168,13 +269,15 @@ public class TlsUtils public static void writeOpaque8(byte[] buf, OutputStream output) throws IOException { - writeUint8((short)buf.length, output); + 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); } @@ -182,6 +285,7 @@ public class TlsUtils public static void writeOpaque24(byte[] buf, OutputStream output) throws IOException { + checkUint24(buf.length); writeUint24(buf.length, output); output.write(buf); } @@ -195,6 +299,32 @@ public class TlsUtils } } + 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 { @@ -204,6 +334,56 @@ public class TlsUtils } } + 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 { @@ -297,6 +477,26 @@ public class TlsUtils 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 { @@ -383,6 +583,12 @@ public class TlsUtils 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 { @@ -395,6 +601,36 @@ public class TlsUtils 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); @@ -412,7 +648,6 @@ public class TlsUtils } public static void writeVersion(ProtocolVersion version, byte[] buf, int offset) - throws IOException { buf[offset] = (byte)version.getMajorVersion(); buf[offset + 1] = (byte)version.getMinorVersion(); @@ -433,6 +668,31 @@ public class TlsUtils 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()); @@ -461,17 +721,8 @@ public class TlsUtils public static Vector getSignatureAlgorithmsExtension(Hashtable extensions) throws IOException { - - if (extensions == null) - { - return null; - } - byte[] extensionValue = (byte[])extensions.get(EXT_signature_algorithms); - if (extensionValue == null) - { - return null; - } - return readSignatureAlgorithmsExtension(extensionValue); + byte[] extensionData = getExtensionData(extensions, EXT_signature_algorithms); + return extensionData == null ? null : readSignatureAlgorithmsExtension(extensionData); } /** @@ -484,61 +735,94 @@ public class TlsUtils public static byte[] createSignatureAlgorithmsExtension(Vector supportedSignatureAlgorithms) 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)"); - } - ByteArrayOutputStream buf = new ByteArrayOutputStream(); // supported_signature_algorithms - TlsUtils.writeUint16(2 * supportedSignatureAlgorithms.size(), buf); - for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i) - { - SignatureAndHashAlgorithm entry = (SignatureAndHashAlgorithm)supportedSignatureAlgorithms.elementAt(i); - entry.encode(buf); - } + encodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, buf); return buf.toByteArray(); } /** - * Read a 'signature_algorithms' extension value. + * Read 'signature_algorithms' extension data. * - * @param extensionValue The extension value. + * @param extensionData The extension data. * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}. * @throws IOException */ - public static Vector readSignatureAlgorithmsExtension(byte[] extensionValue) + public static Vector readSignatureAlgorithmsExtension(byte[] extensionData) throws IOException { + if (extensionData == null) + { + throw new IllegalArgumentException("'extensionData' cannot be null"); + } + + ByteArrayInputStream buf = new ByteArrayInputStream(extensionData); - if (extensionValue == null) + // 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("'extensionValue' cannot be null"); + throw new IllegalArgumentException( + "'supportedSignatureAlgorithms' must have length from 1 to (2^15 - 1)"); } - ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue); + // 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(buf); + int length = TlsUtils.readUint16(input); if (length < 2 || (length & 1) != 0) { throw new TlsFatalAlert(AlertDescription.decode_error); } int count = length / 2; - Vector result = new Vector(count); + Vector supportedSignatureAlgorithms = new Vector(count); for (int i = 0; i < count; ++i) { - SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.parse(buf); - result.addElement(entry); + 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); } - - TlsProtocol.assertEmpty(buf); - - return result; + return supportedSignatureAlgorithms; } public static byte[] PRF(TlsContext context, byte[] secret, String asciiLabel, byte[] seed, int size) @@ -557,12 +841,7 @@ public class TlsUtils if (prfAlgorithm == PRFAlgorithm.tls_prf_legacy) { - if (!ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(version.getEquivalentTLSVersion())) - { - return PRF_legacy(secret, label, labelSeed, size); - } - - prfAlgorithm = PRFAlgorithm.tls_prf_sha256; + return PRF_legacy(secret, label, labelSeed, size); } Digest prfDigest = createPRFHash(prfAlgorithm); @@ -646,7 +925,7 @@ public class TlsUtils byte[] seed = concat(securityParameters.getServerRandom(), securityParameters.getClientRandom()); - if (context.getServerVersion().isSSL()) + if (isSSL(context)) { return calculateKeyBlock_SSL(master_secret, seed, size); } @@ -690,7 +969,7 @@ public class TlsUtils SecurityParameters securityParameters = context.getSecurityParameters(); byte[] seed = concat(securityParameters.getClientRandom(), securityParameters.getServerRandom()); - if (context.getServerVersion().isSSL()) + if (isSSL(context)) { return calculateMasterSecret_SSL(pre_master_secret, seed); } @@ -729,7 +1008,7 @@ public class TlsUtils static byte[] calculateVerifyData(TlsContext context, String asciiLabel, byte[] handshakeHash) { - if (context.getServerVersion().isSSL()) + if (isSSL(context)) { return handshakeHash; } @@ -741,7 +1020,7 @@ public class TlsUtils return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length); } - public static final Digest createHash(int hashAlgorithm) + public static final Digest createHash(short hashAlgorithm) { switch (hashAlgorithm) { @@ -762,7 +1041,7 @@ public class TlsUtils } } - public static final Digest cloneHash(int hashAlgorithm, Digest hash) + public static final Digest cloneHash(short hashAlgorithm, Digest hash) { switch (hashAlgorithm) { @@ -820,7 +1099,7 @@ public class TlsUtils } } - public static ASN1ObjectIdentifier getOIDForHashAlgorithm(int hashAlgorithm) + public static ASN1ObjectIdentifier getOIDForHashAlgorithm(short hashAlgorithm) { switch (hashAlgorithm) { @@ -912,6 +1191,20 @@ public class TlsUtils 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) diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java index f3dd59e..d5f0769 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java @@ -7,18 +7,16 @@ 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; - private final static int MIN_IP_OVERHEAD = 20; - private final static int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64; - private final static int UDP_OVERHEAD = 8; - - private final DatagramSocket socket; - private final int receiveLimit, sendLimit; + 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"); @@ -62,7 +60,7 @@ public class UDPTransport * the DTLS implementation SHOULD generate an error, thus avoiding sending a packet * which will be fragmented." */ - // TODO Exception + throw new TlsFatalAlert(AlertDescription.internal_error); } DatagramPacket packet = new DatagramPacket(buf, off, len); diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java new file mode 100644 index 0000000..c32a904 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java @@ -0,0 +1,104 @@ +package org.bouncycastle.crypto.tls; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java index bfa304b..6bf3399 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java @@ -10,7 +10,6 @@ import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; -import org.bouncycastle.asn1.nist.NISTNamedCurves; import org.bouncycastle.asn1.oiw.ElGamalParameter; import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.pkcs.DHParameter; @@ -18,11 +17,9 @@ import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.pkcs.RSAPrivateKey; import org.bouncycastle.asn1.sec.ECPrivateKey; -import org.bouncycastle.asn1.sec.SECNamedCurves; -import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.DSAParameter; -import org.bouncycastle.asn1.x9.X962NamedCurves; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.X962Parameters; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; @@ -130,22 +127,7 @@ public class PrivateKeyFactory if (params.isNamedCurve()) { ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); - x9 = X962NamedCurves.getByOID(oid); - - if (x9 == null) - { - x9 = SECNamedCurves.getByOID(oid); - - if (x9 == null) - { - x9 = NISTNamedCurves.getByOID(oid); - - if (x9 == null) - { - x9 = TeleTrusTNamedCurves.getByOID(oid); - } - } - } + x9 = ECNamedCurveTable.getByOID(oid); } else { diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java index ab52802..7b06c3f 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java @@ -2,17 +2,23 @@ package org.bouncycastle.crypto.util; import java.io.IOException; +import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.pkcs.RSAPrivateKey; +import org.bouncycastle.asn1.sec.ECPrivateKey; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.DSAParameter; +import org.bouncycastle.asn1.x9.X962Parameters; +import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.DSAParameters; import org.bouncycastle.crypto.params.DSAPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.RSAKeyParameters; import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; @@ -43,6 +49,31 @@ public class PrivateKeyInfoFactory 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/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java index 343bbd3..2d2927b 100644 --- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java +++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java @@ -12,14 +12,11 @@ import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.nist.NISTNamedCurves; import org.bouncycastle.asn1.oiw.ElGamalParameter; import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.pkcs.DHParameter; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.RSAPublicKey; -import org.bouncycastle.asn1.sec.SECNamedCurves; -import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.DSAParameter; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; @@ -27,7 +24,7 @@ import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.asn1.x9.DHDomainParameters; import org.bouncycastle.asn1.x9.DHPublicKey; import org.bouncycastle.asn1.x9.DHValidationParms; -import org.bouncycastle.asn1.x9.X962NamedCurves; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.X962Parameters; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.asn1.x9.X9ECPoint; @@ -160,29 +157,13 @@ public class PublicKeyFactory } else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey)) { - X962Parameters params = new X962Parameters( - (ASN1Primitive)algId.getParameters()); + X962Parameters params = X962Parameters.getInstance(algId.getParameters()); X9ECParameters x9; if (params.isNamedCurve()) { ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); - x9 = X962NamedCurves.getByOID(oid); - - if (x9 == null) - { - x9 = SECNamedCurves.getByOID(oid); - - if (x9 == null) - { - x9 = NISTNamedCurves.getByOID(oid); - - if (x9 == null) - { - x9 = TeleTrusTNamedCurves.getByOID(oid); - } - } - } + x9 = ECNamedCurveTable.getByOID(oid); } else { diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html deleted file mode 100644 index 787b892..0000000 --- a/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Some general utility/conversion classes. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/i18n/LocaleString.java b/bcprov/src/main/java/org/bouncycastle/i18n/LocaleString.java index b9e2232..ab898a9 100644 --- a/bcprov/src/main/java/org/bouncycastle/i18n/LocaleString.java +++ b/bcprov/src/main/java/org/bouncycastle/i18n/LocaleString.java @@ -15,6 +15,12 @@ public class LocaleString extends LocalizedMessage { 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) { diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/JcaJceUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/JcaJceUtils.java new file mode 100644 index 0000000..d7677f3 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/JcaJceUtils.java @@ -0,0 +1,53 @@ +package org.bouncycastle.jcajce; + +import java.io.IOException; +import java.security.AlgorithmParameters; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Primitive; + +public class JcaJceUtils +{ + private JcaJceUtils() + { + + } + + /** + * Extract an ASN.1 encodable from an AlgorithmParameters object. + * + * @param params the object to get the encoding used to create the return value. + * @return an ASN.1 object representing the primitives making up the params parameter. + * @throws IOException if an encoding cannot be extracted. + */ + public static ASN1Encodable extractParameters(AlgorithmParameters params) + throws IOException + { + // we try ASN.1 explicitly first just in case and then role back to the default. + ASN1Encodable asn1Params; + try + { + asn1Params = ASN1Primitive.fromByteArray(params.getEncoded("ASN.1")); + } + catch (Exception ex) + { + asn1Params = ASN1Primitive.fromByteArray(params.getEncoded()); + } + + return asn1Params; + } + + public static void loadParameters(AlgorithmParameters params, ASN1Encodable sParams) + throws IOException + { + // we try ASN.1 explicitly first just in case and then role back to the default. + try + { + params.init(sParams.toASN1Primitive().getEncoded(), "ASN.1"); + } + catch (Exception ex) + { + params.init(sParams.toASN1Primitive().getEncoded()); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherInputStream.java b/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherInputStream.java new file mode 100644 index 0000000..84291ba --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherInputStream.java @@ -0,0 +1,217 @@ +package org.bouncycastle.jcajce.io; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; + +import org.bouncycastle.crypto.io.InvalidCipherTextIOException; + +/** + * 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. + * <p/> + * 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. + * <p/> + * This is a reimplementation of {@link javax.crypto.CipherInputStream} that is safe for use with + * AEAD block ciphers, and does not silently catch {@link BadPaddingException} and + * {@link IllegalBlockSizeException} errors. Any errors that occur during {@link Cipher#doFinal() + * finalisation} are rethrown wrapped in an {@link InvalidCipherTextIOException}. + */ +public class CipherInputStream + extends FilterInputStream +{ + private final Cipher cipher; + private final byte[] inputBuffer = new byte[512]; + private boolean finalized = false; + private byte[] buf; + private int maxBuf; + private int bufOff; + + /** + * Constructs a CipherInputStream from an InputStream and an initialised Cipher. + */ + public CipherInputStream(InputStream input, Cipher cipher) + { + super(input); + this.cipher = cipher; + } + + /** + * 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(inputBuffer); + if (read == -1) + { + buf = finaliseCipher(); + if ((buf == null) || (buf.length == 0)) + { + return -1; + } + maxBuf = buf.length; + return maxBuf; + } + + buf = cipher.update(inputBuffer, 0, read); + if (buf != null) + { + maxBuf = buf.length; + } + } + return maxBuf; + } + + private byte[] finaliseCipher() + throws InvalidCipherTextIOException + { + try + { + finalized = true; + return cipher.doFinal(); + } + catch (GeneralSecurityException e) + { + throw new InvalidCipherTextIOException("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. + * <p/> + * 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 <code>len</code> bytes in the provided array. + * <p/> + * 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 <code>b</code> + * @param len the maximum number of bytes read. + * @return the total number of bytes read into the buffer, or <code>-1</code> 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 then 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/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherOutputStream.java b/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherOutputStream.java new file mode 100644 index 0000000..814b339 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherOutputStream.java @@ -0,0 +1,147 @@ +package org.bouncycastle.jcajce.io; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.GeneralSecurityException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; + +import org.bouncycastle.crypto.io.InvalidCipherTextIOException; + +/** + * 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. + * <p/> + * For example, if the cipher is initialized for encryption, the CipherOutputStream will encrypt the + * data before writing the encrypted data to the underlying stream. + * <p/> + * This is a reimplementation of {@link javax.crypto.CipherOutputStream} that is safe for use with + * AEAD block ciphers, and does not silently catch {@link BadPaddingException} and + * {@link IllegalBlockSizeException} errors. Any errors that occur during {@link Cipher#doFinal() + * finalisation} are rethrown wrapped in an {@link InvalidCipherTextIOException}. + */ +public class CipherOutputStream + extends FilterOutputStream +{ + private final Cipher cipher; + private final byte[] oneByte = new byte[1]; + + /** + * Constructs a CipherOutputStream from an OutputStream and a Cipher. + */ + public CipherOutputStream(OutputStream output, Cipher cipher) + { + super(output); + this.cipher = cipher; + } + + /** + * Writes the specified byte to this output stream. + * + * @param b the <code>byte</code>. + * @throws java.io.IOException if an I/O error occurs. + */ + public void write(int b) + throws IOException + { + oneByte[0] = (byte)b; + write(oneByte, 0, 1); + } + + /** + * Writes <code>len</code> bytes from the specified byte array starting at offset + * <code>off</code> 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 + { + byte[] outData = cipher.update(b, off, len); + if (outData != null) + { + out.write(outData); + } + } + + /** + * Flushes this output stream by forcing any buffered output bytes that have already been + * processed by the encapsulated cipher object to be written out. + * <p/> + * <p/> + * 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 <code>write</code> 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. + * <p/> + * This method invokes the <code>doFinal</code> 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 <code>flush</code> method of this output stream. + * <p/> + * This method resets the encapsulated cipher object to its initial state and calls the + * <code>close</code> 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 + { + IOException error = null; + try + { + byte[] outData = cipher.doFinal(); + if (outData != null) + { + out.write(outData); + } + } + catch (GeneralSecurityException e) + { + error = new InvalidCipherTextIOException("Error during cipher finalisation", 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/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java index 3e16254..2efffbf 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java @@ -32,6 +32,13 @@ public class DSA provider.addAlgorithm("Alg.Alias.Signature.RAWDSA", "NONEWITHDSA"); + provider.addAlgorithm("Signature.DETDSA", PREFIX + "DSASigner$detDSA"); + provider.addAlgorithm("Signature.SHA1WITHDETDSA", PREFIX + "DSASigner$detDSA"); + provider.addAlgorithm("Signature.SHA224WITHDETDSA", PREFIX + "DSASigner$detDSA224"); + provider.addAlgorithm("Signature.SHA256WITHDETDSA", PREFIX + "DSASigner$detDSA256"); + provider.addAlgorithm("Signature.SHA384WITHDETDSA", PREFIX + "DSASigner$detDSA384"); + provider.addAlgorithm("Signature.SHA512WITHDETDSA", PREFIX + "DSASigner$detDSA512"); + addSignatureAlgorithm(provider, "SHA224", "DSA", PREFIX + "DSASigner$dsa224", NISTObjectIdentifiers.dsa_with_sha224); addSignatureAlgorithm(provider, "SHA256", "DSA", PREFIX + "DSASigner$dsa256", NISTObjectIdentifiers.dsa_with_sha256); addSignatureAlgorithm(provider, "SHA384", "DSA", PREFIX + "DSASigner$dsa384", NISTObjectIdentifiers.dsa_with_sha384); diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java index 4c2ca28..d06e05c 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java @@ -67,6 +67,13 @@ public class EC provider.addAlgorithm("Alg.Alias.Signature.1.2.840.10045.4.1", "ECDSA"); provider.addAlgorithm("Alg.Alias.Signature." + TeleTrusTObjectIdentifiers.ecSignWithSha1, "ECDSA"); + provider.addAlgorithm("Signature.DETECDSA", PREFIX + "SignatureSpi$ecDetDSA"); + provider.addAlgorithm("Signature.SHA1WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA"); + provider.addAlgorithm("Signature.SHA224WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA224"); + provider.addAlgorithm("Signature.SHA256WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA256"); + provider.addAlgorithm("Signature.SHA384WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA384"); + provider.addAlgorithm("Signature.SHA512WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA512"); + addSignatureAlgorithm(provider, "SHA224", "ECDSA", PREFIX + "SignatureSpi$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224); addSignatureAlgorithm(provider, "SHA256", "ECDSA", PREFIX + "SignatureSpi$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256); addSignatureAlgorithm(provider, "SHA384", "ECDSA", PREFIX + "SignatureSpi$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384); diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java index c9462a6..f2b5314 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java @@ -50,17 +50,34 @@ public class KeyAgreementSpi private byte[] bigIntToBytes( BigInteger r) { + // + // RFC 2631 (2.1.2) specifies that the secret should be padded with leading zeros if necessary + // must be the same length as p + // + int expectedLength = (p.bitLength() + 7) / 8; + byte[] tmp = r.toByteArray(); - - if (tmp[0] == 0) + + if (tmp.length == expectedLength) + { + return tmp; + } + + if (tmp[0] == 0 && tmp.length == expectedLength + 1) { - byte[] ntmp = new byte[tmp.length - 1]; + byte[] rv = new byte[tmp.length - 1]; - System.arraycopy(tmp, 1, ntmp, 0, ntmp.length); - return ntmp; + System.arraycopy(tmp, 1, rv, 0, rv.length); + return rv; } - - return tmp; + + // tmp must be shorter than expectedLength + // pad to the left with zeros. + byte[] rv = new byte[expectedLength]; + + System.arraycopy(tmp, 0, rv, rv.length - tmp.length, tmp.length); + + return rv; } protected Key engineDoPhase( diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java index ef12b4f..ade49b3 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java @@ -29,6 +29,7 @@ import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA384Digest; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.signers.HMacDSAKCalculator; public class DSASigner extends SignatureSpi @@ -220,6 +221,15 @@ public class DSASigner } } + static public class detDSA + extends DSASigner + { + public detDSA() + { + super(new SHA1Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA1Digest()))); + } + } + static public class dsa224 extends DSASigner { @@ -228,7 +238,16 @@ public class DSASigner super(new SHA224Digest(), new org.bouncycastle.crypto.signers.DSASigner()); } } - + + static public class detDSA224 + extends DSASigner + { + public detDSA224() + { + super(new SHA224Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA224Digest()))); + } + } + static public class dsa256 extends DSASigner { @@ -237,7 +256,16 @@ public class DSASigner super(new SHA256Digest(), new org.bouncycastle.crypto.signers.DSASigner()); } } - + + static public class detDSA256 + extends DSASigner + { + public detDSA256() + { + super(new SHA256Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA256Digest()))); + } + } + static public class dsa384 extends DSASigner { @@ -246,7 +274,16 @@ public class DSASigner super(new SHA384Digest(), new org.bouncycastle.crypto.signers.DSASigner()); } } - + + static public class detDSA384 + extends DSASigner + { + public detDSA384() + { + super(new SHA384Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA384Digest()))); + } + } + static public class dsa512 extends DSASigner { @@ -256,6 +293,15 @@ public class DSASigner } } + static public class detDSA512 + extends DSASigner + { + public detDSA512() + { + super(new SHA512Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA512Digest()))); + } + } + static public class noneDSA extends DSASigner { diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java index 56fe741..9b7e797 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java @@ -119,8 +119,8 @@ public class BCDSTU4145PrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), dp.getN(), dp.getH().intValue()); } @@ -150,8 +150,8 @@ public class BCDSTU4145PrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), dp.getN(), dp.getH().intValue()); } @@ -162,8 +162,8 @@ public class BCDSTU4145PrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), spec.getN(), spec.getH().intValue()); } @@ -206,8 +206,8 @@ public class BCDSTU4145PrivateKey oid.getId(), ellipticCurve, new ECPoint( - gParam.getG().getX().toBigInteger(), - gParam.getG().getY().toBigInteger()), + gParam.getG().getAffineXCoord().toBigInteger(), + gParam.getG().getAffineYCoord().toBigInteger()), gParam.getN(), gParam.getH()); } @@ -219,8 +219,8 @@ public class BCDSTU4145PrivateKey ECUtil.getCurveName(oid), ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH()); } @@ -237,8 +237,8 @@ public class BCDSTU4145PrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH().intValue()); } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java index a060ae6..c641ee9 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java @@ -16,7 +16,6 @@ import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERBitString; -import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.ua.DSTU4145BinaryField; import org.bouncycastle.asn1.ua.DSTU4145ECBinary; @@ -28,13 +27,9 @@ import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x9.X962Parameters; import org.bouncycastle.asn1.x9.X9ECParameters; -import org.bouncycastle.asn1.x9.X9ECPoint; -import org.bouncycastle.asn1.x9.X9IntegerConverter; -import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; -import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil; import org.bouncycastle.jce.interfaces.ECPointEncoder; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -88,7 +83,7 @@ public class BCDSTU4145PublicKey { org.bouncycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); - q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false); + q = s.getCurve().createPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); } this.ecSpec = null; } @@ -157,8 +152,8 @@ public class BCDSTU4145PublicKey return new ECParameterSpec( ellipticCurve, new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), dp.getN(), dp.getH().intValue()); } @@ -191,155 +186,79 @@ public class BCDSTU4145PublicKey private void populateFromPubKeyInfo(SubjectPublicKeyInfo info) { - if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145be) || info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) - { - DERBitString bits = info.getPublicKeyData(); - ASN1OctetString key; - this.algorithm = "DSTU4145"; - - try - { - key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes()); - } - catch (IOException ex) - { - throw new IllegalArgumentException("error recovering public key"); - } - - byte[] keyEnc = key.getOctets(); - - if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) - { - reverseBytes(keyEnc); - } - - dstuParams = DSTU4145Params.getInstance((ASN1Sequence)info.getAlgorithm().getParameters()); + DERBitString bits = info.getPublicKeyData(); + ASN1OctetString key; + this.algorithm = "DSTU4145"; - //ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); - org.bouncycastle.jce.spec.ECParameterSpec spec = null; - if (dstuParams.isNamedCurve()) - { - ASN1ObjectIdentifier curveOid = dstuParams.getNamedCurve(); - ECDomainParameters ecP = DSTU4145NamedCurves.getByOID(curveOid); + try + { + key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes()); + } + catch (IOException ex) + { + throw new IllegalArgumentException("error recovering public key"); + } - spec = new ECNamedCurveParameterSpec(curveOid.getId(), ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed()); - } - else - { - DSTU4145ECBinary binary = dstuParams.getECBinary(); - byte[] b_bytes = binary.getB(); - if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) - { - reverseBytes(b_bytes); - } - DSTU4145BinaryField field = binary.getField(); - ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), new BigInteger(1, b_bytes)); - byte[] g_bytes = binary.getG(); - if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) - { - reverseBytes(g_bytes); - } - spec = new org.bouncycastle.jce.spec.ECParameterSpec(curve, DSTU4145PointEncoder.decodePoint(curve, g_bytes), binary.getN()); - } + byte[] keyEnc = key.getOctets(); - ECCurve curve = spec.getCurve(); - EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); + if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) + { + reverseBytes(keyEnc); + } - //this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false); - this.q = DSTU4145PointEncoder.decodePoint(curve, keyEnc); + dstuParams = DSTU4145Params.getInstance((ASN1Sequence)info.getAlgorithm().getParameters()); - if (dstuParams.isNamedCurve()) - { - ecSpec = new ECNamedCurveSpec( - dstuParams.getNamedCurve().getId(), - ellipticCurve, - new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), - spec.getN(), spec.getH()); - } - else - { - ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), - spec.getN(), spec.getH().intValue()); - } + //ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); + org.bouncycastle.jce.spec.ECParameterSpec spec = null; + if (dstuParams.isNamedCurve()) + { + ASN1ObjectIdentifier curveOid = dstuParams.getNamedCurve(); + ECDomainParameters ecP = DSTU4145NamedCurves.getByOID(curveOid); + spec = new ECNamedCurveParameterSpec(curveOid.getId(), ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed()); } else { - X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithm().getParameters()); - ECCurve curve; - EllipticCurve ellipticCurve; - - if (params.isNamedCurve()) - { - ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); - X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); - - curve = ecP.getCurve(); - ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed()); - - ecSpec = new ECNamedCurveSpec( - ECUtil.getCurveName(oid), - ellipticCurve, - new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), - ecP.getN(), - ecP.getH()); - } - else if (params.isImplicitlyCA()) + DSTU4145ECBinary binary = dstuParams.getECBinary(); + byte[] b_bytes = binary.getB(); + if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) { - ecSpec = null; - curve = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa().getCurve(); + reverseBytes(b_bytes); } - else + DSTU4145BinaryField field = binary.getField(); + ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), new BigInteger(1, b_bytes)); + byte[] g_bytes = binary.getG(); + if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le)) { - X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); - - curve = ecP.getCurve(); - ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed()); - - this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), - ecP.getN(), - ecP.getH().intValue()); + reverseBytes(g_bytes); } + spec = new org.bouncycastle.jce.spec.ECParameterSpec(curve, DSTU4145PointEncoder.decodePoint(curve, g_bytes), binary.getN()); + } - DERBitString bits = info.getPublicKeyData(); - byte[] data = bits.getBytes(); - ASN1OctetString key = new DEROctetString(data); + ECCurve curve = spec.getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); - // - // extra octet string - one of our old certs... - // - if (data[0] == 0x04 && data[1] == data.length - 2 - && (data[2] == 0x02 || data[2] == 0x03)) - { - int qLength = new X9IntegerConverter().getByteLength(curve); - - if (qLength >= data.length - 3) - { - try - { - key = (ASN1OctetString)ASN1Primitive.fromByteArray(data); - } - catch (IOException ex) - { - throw new IllegalArgumentException("error recovering public key"); - } - } - } - X9ECPoint derQ = new X9ECPoint(curve, key); + //this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false); + this.q = DSTU4145PointEncoder.decodePoint(curve, keyEnc); - this.q = derQ.getPoint(); + if (dstuParams.isNamedCurve()) + { + ecSpec = new ECNamedCurveSpec( + dstuParams.getNamedCurve().getId(), + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), spec.getH()); + } + else + { + ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), spec.getH().intValue()); } } @@ -370,61 +289,18 @@ public class BCDSTU4145PublicKey ASN1Encodable params; SubjectPublicKeyInfo info; - if (algorithm.equals("DSTU4145")) + if (dstuParams != null) { - if (dstuParams != null) - { - params = dstuParams; - } - else - { - if (ecSpec instanceof ECNamedCurveSpec) - { - params = new DSTU4145Params(new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName())); - } - else - { // strictly speaking this may not be applicable... - ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); - - X9ECParameters ecP = new X9ECParameters( - curve, - EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), - ecSpec.getOrder(), - BigInteger.valueOf(ecSpec.getCofactor()), - ecSpec.getCurve().getSeed()); - - params = new X962Parameters(ecP); - } - } - - byte[] encKey = DSTU4145PointEncoder.encodePoint(this.q); - - try - { - info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(UAObjectIdentifiers.dstu4145be, params), new DEROctetString(encKey)); - } - catch (IOException e) - { - return null; - } + params = dstuParams; } else { if (ecSpec instanceof ECNamedCurveSpec) { - ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); - if (curveOid == null) - { - curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); - } - params = new X962Parameters(curveOid); - } - else if (ecSpec == null) - { - params = new X962Parameters(DERNull.INSTANCE); + params = new DSTU4145Params(new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName())); } else - { + { // strictly speaking this may not be applicable... ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); X9ECParameters ecP = new X9ECParameters( @@ -436,12 +312,17 @@ public class BCDSTU4145PublicKey params = new X962Parameters(ecP); } + } - ECCurve curve = this.engineGetQ().getCurve(); - ASN1OctetString p = (ASN1OctetString) - new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive(); + byte[] encKey = DSTU4145PointEncoder.encodePoint(this.q); - info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); + try + { + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(UAObjectIdentifiers.dstu4145be, params), new DEROctetString(encKey)); + } + catch (IOException e) + { + return null; } return KeyUtil.getEncodedSubjectPublicKeyInfo(info); @@ -464,7 +345,7 @@ public class BCDSTU4145PublicKey public ECPoint getW() { - return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger()); + return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); } public org.bouncycastle.math.ec.ECPoint getQ() @@ -473,11 +354,11 @@ public class BCDSTU4145PublicKey { if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp) { - return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY()); + return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord()); } else { - return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY()); + return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord()); } } @@ -505,8 +386,8 @@ public class BCDSTU4145PublicKey String nl = System.getProperty("line.separator"); buf.append("EC Public Key").append(nl); - buf.append(" X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl); - buf.append(" Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl); + buf.append(" X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl); + buf.append(" Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl); return buf.toString(); } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java index ac04d3c..45d5b08 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java @@ -13,14 +13,11 @@ import java.util.Enumeration; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.DERBitString; -import org.bouncycastle.asn1.DERInteger; import org.bouncycastle.asn1.DERNull; -import org.bouncycastle.asn1.DERObjectIdentifier; -import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; -import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; @@ -138,8 +135,8 @@ public class BCECPrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), dp.getN(), dp.getH().intValue()); } @@ -171,22 +168,16 @@ public class BCECPrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), dp.getN(), dp.getH().intValue()); } else { EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); - - this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), - spec.getN(), - spec.getH().intValue()); + + this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec); } publicKey = getPublicKeyDetails(pubKey); @@ -223,34 +214,16 @@ public class BCECPrivateKey { ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); - - if (ecP == null) // GOST Curve - { - ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid); - EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed()); - - ecSpec = new ECNamedCurveSpec( - ECGOST3410NamedCurves.getName(oid), - ellipticCurve, - new ECPoint( - gParam.getG().getX().toBigInteger(), - gParam.getG().getY().toBigInteger()), - gParam.getN(), - gParam.getH()); - } - else - { - EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); - - ecSpec = new ECNamedCurveSpec( - ECUtil.getCurveName(oid), - ellipticCurve, - new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), - ecP.getN(), - ecP.getH()); - } + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + + ecSpec = new ECNamedCurveSpec( + ECUtil.getCurveName(oid), + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH()); } else if (params.isImplicitlyCA()) { @@ -264,16 +237,16 @@ public class BCECPrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH().intValue()); } ASN1Encodable privKey = info.parsePrivateKey(); - if (privKey instanceof DERInteger) + if (privKey instanceof ASN1Integer) { - DERInteger derD = DERInteger.getInstance(privKey); + ASN1Integer derD = ASN1Integer.getInstance(privKey); this.d = derD.getValue(); } @@ -313,11 +286,12 @@ public class BCECPrivateKey if (ecSpec instanceof ECNamedCurveSpec) { - DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); + ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); if (curveOid == null) // guess it's the OID { - curveOid = new DERObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); } + params = new X962Parameters(curveOid); } else if (ecSpec == null) @@ -352,15 +326,7 @@ public class BCECPrivateKey try { - if (algorithm.equals("ECGOST3410")) - { - info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive()); - } - else - { - - info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params.toASN1Primitive()), keyStructure.toASN1Primitive()); - } + info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure); return info.getEncoded(ASN1Encoding.DER); } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java index 2b61727..0eaae1d 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java @@ -90,7 +90,7 @@ public class BCECPublicKey { org.bouncycastle.jce.spec.ECParameterSpec s = configuration.getEcImplicitlyCa(); - q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false); + q = s.getCurve().createPoint(q.getXCoord().toBigInteger(), q.getYCoord().toBigInteger(), false); } this.ecSpec = null; } @@ -188,8 +188,8 @@ public class BCECPublicKey return new ECParameterSpec( ellipticCurve, new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), dp.getN(), dp.getH().intValue()); } @@ -212,8 +212,8 @@ public class BCECPublicKey ECUtil.getCurveName(oid), ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH()); } @@ -232,8 +232,8 @@ public class BCECPublicKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH().intValue()); } @@ -310,8 +310,19 @@ public class BCECPublicKey } ECCurve curve = this.engineGetQ().getCurve(); - ASN1OctetString p = (ASN1OctetString) - new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive(); + ASN1OctetString p; + + // stored curve is null if ImplicitlyCa + if (ecSpec == null) + { + p = (ASN1OctetString) + new X9ECPoint(curve.createPoint(this.getQ().getXCoord().toBigInteger(), this.getQ().getYCoord().toBigInteger(), withCompression)).toASN1Primitive(); + } + else + { + p = (ASN1OctetString) + new X9ECPoint(curve.createPoint(this.getQ().getAffineXCoord().toBigInteger(), this.getQ().getAffineYCoord().toBigInteger(), withCompression)).toASN1Primitive(); + } info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); @@ -351,7 +362,7 @@ public class BCECPublicKey public ECPoint getW() { - return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger()); + return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); } public org.bouncycastle.math.ec.ECPoint getQ() @@ -360,11 +371,11 @@ public class BCECPublicKey { if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp) { - return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY()); + return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord()); } else { - return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY()); + return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord()); } } @@ -392,8 +403,8 @@ public class BCECPublicKey String nl = System.getProperty("line.separator"); buf.append("EC Public Key").append(nl); - buf.append(" X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl); - buf.append(" Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl); + buf.append(" X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl); + buf.append(" Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl); return buf.toString(); diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java index c609d95..0556378 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java @@ -15,7 +15,7 @@ import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.spec.SecretKeySpec; -import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x9.X9IntegerConverter; @@ -76,7 +76,7 @@ public class KeyAgreementSpi private byte[] bigIntToBytes( BigInteger r) { - return converter.integerToBytes(r, converter.getByteLength(parameters.getG().getX())); + return converter.integerToBytes(r, converter.getByteLength(parameters.getG().getAffineXCoord())); } protected KeyAgreementSpi( @@ -185,7 +185,7 @@ public class KeyAgreementSpi int keySize = ((Integer)algorithms.get(algorithm)).intValue(); - DHKDFParameters params = new DHKDFParameters(new DERObjectIdentifier(algorithm), keySize, secret); + DHKDFParameters params = new DHKDFParameters(new ASN1ObjectIdentifier(algorithm), keySize, secret); byte[] keyBytes = new byte[keySize / 8]; kdf.init(params); diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java index 5e1a8a3..f47f8a2 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java @@ -10,10 +10,7 @@ import java.security.spec.ECGenParameterSpec; import java.util.Hashtable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.nist.NISTNamedCurves; -import org.bouncycastle.asn1.sec.SECNamedCurves; -import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves; -import org.bouncycastle.asn1.x9.X962NamedCurves; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; @@ -148,47 +145,23 @@ public abstract class KeyPairGeneratorSpi curveName = ((ECNamedCurveGenParameterSpec)params).getName(); } - X9ECParameters ecP = X962NamedCurves.getByName(curveName); + X9ECParameters ecP = ECNamedCurveTable.getByName(curveName); if (ecP == null) { - ecP = SECNamedCurves.getByName(curveName); - if (ecP == null) + // See if it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug) + try { - ecP = NISTNamedCurves.getByName(curveName); - } - if (ecP == null) - { - ecP = TeleTrusTNamedCurves.getByName(curveName); - } - if (ecP == null) - { - // See if it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug) - try + ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(curveName); + ecP = ECNamedCurveTable.getByOID(oid); + if (ecP == null) { - ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(curveName); - ecP = X962NamedCurves.getByOID(oid); - if (ecP == null) - { - ecP = SECNamedCurves.getByOID(oid); - } - if (ecP == null) - { - ecP = NISTNamedCurves.getByOID(oid); - } - if (ecP == null) - { - ecP = TeleTrusTNamedCurves.getByOID(oid); - } - if (ecP == null) - { - throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName); - } - } - catch (IllegalArgumentException ex) - { - throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName); + throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName); } } + catch (IllegalArgumentException ex) + { + throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName); + } } this.ecParams = new ECNamedCurveSpec( diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java index 29c50f4..e94746c 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java @@ -25,6 +25,7 @@ import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.crypto.signers.ECNRSigner; +import org.bouncycastle.crypto.signers.HMacDSAKCalculator; import org.bouncycastle.jcajce.provider.asymmetric.util.DSABase; import org.bouncycastle.jcajce.provider.asymmetric.util.DSAEncoder; import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; @@ -73,6 +74,15 @@ public class SignatureSpi } } + static public class ecDetDSA + extends SignatureSpi + { + public ecDetDSA() + { + super(new SHA1Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA1Digest())), new StdDSAEncoder()); + } + } + static public class ecDSAnone extends SignatureSpi { @@ -91,6 +101,15 @@ public class SignatureSpi } } + static public class ecDetDSA224 + extends SignatureSpi + { + public ecDetDSA224() + { + super(new SHA224Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA224Digest())), new StdDSAEncoder()); + } + } + static public class ecDSA256 extends SignatureSpi { @@ -100,6 +119,15 @@ public class SignatureSpi } } + static public class ecDetDSA256 + extends SignatureSpi + { + public ecDetDSA256() + { + super(new SHA256Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())), new StdDSAEncoder()); + } + } + static public class ecDSA384 extends SignatureSpi { @@ -109,6 +137,15 @@ public class SignatureSpi } } + static public class ecDetDSA384 + extends SignatureSpi + { + public ecDetDSA384() + { + super(new SHA384Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA384Digest())), new StdDSAEncoder()); + } + } + static public class ecDSA512 extends SignatureSpi { @@ -118,6 +155,15 @@ public class SignatureSpi } } + static public class ecDetDSA512 + extends SignatureSpi + { + public ecDetDSA512() + { + super(new SHA512Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA512Digest())), new StdDSAEncoder()); + } + } + static public class ecDSARipeMD160 extends SignatureSpi { diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java index 88d81c0..2b1c3fa 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java @@ -14,27 +14,32 @@ import java.util.Enumeration; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.DERInteger; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves; +import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x9.X962Parameters; import org.bouncycastle.asn1.x9.X9ECParameters; -import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; +import org.bouncycastle.jce.ECGOST3410NamedCurveTable; import org.bouncycastle.jce.interfaces.ECPointEncoder; import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; import org.bouncycastle.jce.spec.ECNamedCurveSpec; import org.bouncycastle.math.ec.ECCurve; @@ -43,10 +48,11 @@ public class BCECGOST3410PrivateKey { static final long serialVersionUID = 7245981689601667138L; - private String algorithm = "ECGOST3410"; - private boolean withCompression; + private String algorithm = "ECGOST3410"; + private boolean withCompression; - private transient BigInteger d; + private transient GOST3410PublicKeyAlgParameters gostParams; + private transient BigInteger d; private transient ECParameterSpec ecSpec; private transient DERBitString publicKey; private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl(); @@ -99,6 +105,7 @@ public class BCECGOST3410PrivateKey this.withCompression = key.withCompression; this.attrCarrier = key.attrCarrier; this.publicKey = key.publicKey; + this.gostParams = key.gostParams; } public BCECGOST3410PrivateKey( @@ -107,7 +114,7 @@ public class BCECGOST3410PrivateKey BCECGOST3410PublicKey pubKey, ECParameterSpec spec) { - ECDomainParameters dp = params.getParameters(); + ECDomainParameters dp = params.getParameters(); this.algorithm = algorithm; this.d = params.getD(); @@ -117,18 +124,20 @@ public class BCECGOST3410PrivateKey EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), - dp.getN(), - dp.getH().intValue()); + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); } else { this.ecSpec = spec; } + this.gostParams = pubKey.getGostParams(); + publicKey = getPublicKeyDetails(pubKey); } @@ -138,7 +147,7 @@ public class BCECGOST3410PrivateKey BCECGOST3410PublicKey pubKey, org.bouncycastle.jce.spec.ECParameterSpec spec) { - ECDomainParameters dp = params.getParameters(); + ECDomainParameters dp = params.getParameters(); this.algorithm = algorithm; this.d = params.getD(); @@ -148,26 +157,28 @@ public class BCECGOST3410PrivateKey EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed()); this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), - dp.getN(), - dp.getH().intValue()); + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); } else { EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed()); - + this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), - spec.getN(), - spec.getH().intValue()); + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), + spec.getH().intValue()); } + this.gostParams = pubKey.getGostParams(); + publicKey = getPublicKeyDetails(pubKey); } @@ -190,72 +201,107 @@ public class BCECGOST3410PrivateKey private void populateFromPrivKeyInfo(PrivateKeyInfo info) throws IOException { - X962Parameters params = new X962Parameters((ASN1Primitive)info.getPrivateKeyAlgorithm().getParameters()); + ASN1Primitive p = info.getPrivateKeyAlgorithm().getParameters().toASN1Primitive(); + + if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3)) + { + gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getPrivateKeyAlgorithm().getParameters()); + + ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); - if (params.isNamedCurve()) + ECCurve curve = spec.getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); + + ecSpec = new ECNamedCurveSpec( + ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()), + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), spec.getH()); + + ASN1Encodable privKey = info.parsePrivateKey(); + + byte[] encVal = ASN1OctetString.getInstance(privKey).getOctets(); + byte[] dVal = new byte[encVal.length]; + + for (int i = 0; i != encVal.length; i++) + { + dVal[i] = encVal[encVal.length - 1 - i]; + } + + this.d = new BigInteger(1, dVal); + } + else { - ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); - X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); + // for backwards compatibility + X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters()); - if (ecP == null) // GOST Curve + if (params.isNamedCurve()) { - ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid); - EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed()); + ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters()); + X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); - ecSpec = new ECNamedCurveSpec( + if (ecP == null) // GOST Curve + { + ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid); + EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed()); + + ecSpec = new ECNamedCurveSpec( ECGOST3410NamedCurves.getName(oid), ellipticCurve, new ECPoint( - gParam.getG().getX().toBigInteger(), - gParam.getG().getY().toBigInteger()), + gParam.getG().getAffineXCoord().toBigInteger(), + gParam.getG().getAffineYCoord().toBigInteger()), gParam.getN(), gParam.getH()); - } - else - { - EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + } + else + { + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); - ecSpec = new ECNamedCurveSpec( + ecSpec = new ECNamedCurveSpec( ECUtil.getCurveName(oid), ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH()); + } } - } - else if (params.isImplicitlyCA()) - { - ecSpec = null; - } - else - { - X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); - EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); + else if (params.isImplicitlyCA()) + { + ecSpec = null; + } + else + { + X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); + EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed()); - this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), - ecP.getN(), - ecP.getH().intValue()); - } + this.ecSpec = new ECParameterSpec( + ellipticCurve, + new ECPoint( + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), + ecP.getN(), + ecP.getH().intValue()); + } - ASN1Encodable privKey = info.parsePrivateKey(); - if (privKey instanceof DERInteger) - { - DERInteger derD = DERInteger.getInstance(privKey); + ASN1Encodable privKey = info.parsePrivateKey(); + if (privKey instanceof DERInteger) + { + DERInteger derD = DERInteger.getInstance(privKey); - this.d = derD.getValue(); - } - else - { - org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey); + this.d = derD.getValue(); + } + else + { + org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey); - this.d = ec.getKey(); - this.publicKey = ec.getPublicKey(); + this.d = ec.getKey(); + this.publicKey = ec.getPublicKey(); + } } } @@ -282,64 +328,92 @@ public class BCECGOST3410PrivateKey */ public byte[] getEncoded() { - X962Parameters params; - - if (ecSpec instanceof ECNamedCurveSpec) + if (gostParams != null) { - DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); - if (curveOid == null) // guess it's the OID + byte[] encKey = new byte[32]; + + extractBytes(encKey, 0, this.getS()); + + try { - curveOid = new DERObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); + PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, gostParams), new DEROctetString(encKey)); + + return info.getEncoded(ASN1Encoding.DER); + } + catch (IOException e) + { + return null; } - params = new X962Parameters(curveOid); - } - else if (ecSpec == null) - { - params = new X962Parameters(DERNull.INSTANCE); } else { - ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); + X962Parameters params; - X9ECParameters ecP = new X9ECParameters( - curve, - EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), - ecSpec.getOrder(), - BigInteger.valueOf(ecSpec.getCofactor()), - ecSpec.getCurve().getSeed()); + if (ecSpec instanceof ECNamedCurveSpec) + { + DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); + if (curveOid == null) // guess it's the OID + { + curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); + } + params = new X962Parameters(curveOid); + } + else if (ecSpec == null) + { + params = new X962Parameters(DERNull.INSTANCE); + } + else + { + ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); - params = new X962Parameters(ecP); - } - - PrivateKeyInfo info; - org.bouncycastle.asn1.sec.ECPrivateKey keyStructure; + X9ECParameters ecP = new X9ECParameters( + curve, + EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), + ecSpec.getOrder(), + BigInteger.valueOf(ecSpec.getCofactor()), + ecSpec.getCurve().getSeed()); - if (publicKey != null) - { - keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), publicKey, params); - } - else - { - keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), params); - } + params = new X962Parameters(ecP); + } - try - { - if (algorithm.equals("ECGOST3410")) + PrivateKeyInfo info; + org.bouncycastle.asn1.sec.ECPrivateKey keyStructure; + + if (publicKey != null) { - info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive()); + keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), publicKey, params); } else { + keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), params); + } + + try + { + info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive()); - info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params.toASN1Primitive()), keyStructure.toASN1Primitive()); + return info.getEncoded(ASN1Encoding.DER); } + catch (IOException e) + { + return null; + } + } + } - return info.getEncoded(ASN1Encoding.DER); + private void extractBytes(byte[] encKey, int offSet, BigInteger bI) + { + byte[] val = bI.toByteArray(); + if (val.length < 32) + { + byte[] tmp = new byte[32]; + System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length); + val = tmp; } - catch (IOException e) + + for (int i = 0; i != 32; i++) { - return null; + encKey[offSet + i] = val[val.length - 1 - i]; } } @@ -354,7 +428,7 @@ public class BCECGOST3410PrivateKey { return null; } - + return EC5Util.convertSpec(ecSpec, withCompression); } @@ -377,10 +451,10 @@ public class BCECGOST3410PrivateKey { return d; } - + public void setBagAttribute( ASN1ObjectIdentifier oid, - ASN1Encodable attribute) + ASN1Encodable attribute) { attrCarrier.setBagAttribute(oid, attribute); } @@ -398,7 +472,7 @@ public class BCECGOST3410PrivateKey public void setPointFormat(String style) { - withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); } public boolean equals(Object o) @@ -420,8 +494,8 @@ public class BCECGOST3410PrivateKey public String toString() { - StringBuffer buf = new StringBuffer(); - String nl = System.getProperty("line.separator"); + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); buf.append("EC Private Key").append(nl); buf.append(" S: ").append(this.d.toString(16)).append(nl); diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java index b7a1170..650855e 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java @@ -11,12 +11,9 @@ import java.security.spec.ECPublicKeySpec; import java.security.spec.EllipticCurve; import org.bouncycastle.asn1.ASN1Encodable; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERBitString; -import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves; @@ -25,13 +22,9 @@ import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x9.X962Parameters; import org.bouncycastle.asn1.x9.X9ECParameters; -import org.bouncycastle.asn1.x9.X9ECPoint; -import org.bouncycastle.asn1.x9.X9IntegerConverter; -import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util; -import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil; import org.bouncycastle.jce.ECGOST3410NamedCurveTable; import org.bouncycastle.jce.interfaces.ECPointEncoder; @@ -45,12 +38,12 @@ public class BCECGOST3410PublicKey { static final long serialVersionUID = 7026240464295649314L; - private String algorithm = "ECGOST3410"; - private boolean withCompression; + private String algorithm = "ECGOST3410"; + private boolean withCompression; private transient org.bouncycastle.math.ec.ECPoint q; - private transient ECParameterSpec ecSpec; - private transient GOST3410PublicKeyAlgParameters gostParams; + private transient ECParameterSpec ecSpec; + private transient GOST3410PublicKeyAlgParameters gostParams; public BCECGOST3410PublicKey( BCECGOST3410PublicKey key) @@ -60,7 +53,7 @@ public class BCECGOST3410PublicKey this.withCompression = key.withCompression; this.gostParams = key.gostParams; } - + public BCECGOST3410PublicKey( ECPublicKeySpec spec) { @@ -86,18 +79,18 @@ public class BCECGOST3410PublicKey { org.bouncycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); - q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false); - } + q = s.getCurve().createPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); + } this.ecSpec = null; } } - + public BCECGOST3410PublicKey( String algorithm, ECPublicKeyParameters params, ECParameterSpec spec) { - ECDomainParameters dp = params.getParameters(); + ECDomainParameters dp = params.getParameters(); this.algorithm = algorithm; this.q = params.getQ(); @@ -119,7 +112,7 @@ public class BCECGOST3410PublicKey ECPublicKeyParameters params, org.bouncycastle.jce.spec.ECParameterSpec spec) { - ECDomainParameters dp = params.getParameters(); + ECDomainParameters dp = params.getParameters(); this.algorithm = algorithm; this.q = params.getQ(); @@ -153,14 +146,14 @@ public class BCECGOST3410PublicKey private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp) { return new ECParameterSpec( - ellipticCurve, - new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), - dp.getN(), - dp.getH().intValue()); + ellipticCurve, + new ECPoint( + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), + dp.getN(), + dp.getH().intValue()); } - + public BCECGOST3410PublicKey( ECPublicKey key) { @@ -177,125 +170,49 @@ public class BCECGOST3410PublicKey private void populateFromPubKeyInfo(SubjectPublicKeyInfo info) { - if (info.getAlgorithm().getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3410_2001)) - { - DERBitString bits = info.getPublicKeyData(); - ASN1OctetString key; - this.algorithm = "ECGOST3410"; - - try - { - key = (ASN1OctetString) ASN1Primitive.fromByteArray(bits.getBytes()); - } - catch (IOException ex) - { - throw new IllegalArgumentException("error recovering public key"); - } - - byte[] keyEnc = key.getOctets(); - byte[] x = new byte[32]; - byte[] y = new byte[32]; - - for (int i = 0; i != x.length; i++) - { - x[i] = keyEnc[32 - 1 - i]; - } - - for (int i = 0; i != y.length; i++) - { - y[i] = keyEnc[64 - 1 - i]; - } - - gostParams = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithm().getParameters()); - - ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); - - ECCurve curve = spec.getCurve(); - EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); + DERBitString bits = info.getPublicKeyData(); + ASN1OctetString key; + this.algorithm = "ECGOST3410"; - this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false); + try + { + key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes()); + } + catch (IOException ex) + { + throw new IllegalArgumentException("error recovering public key"); + } - ecSpec = new ECNamedCurveSpec( - ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()), - ellipticCurve, - new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), - spec.getN(), spec.getH()); + byte[] keyEnc = key.getOctets(); + byte[] x = new byte[32]; + byte[] y = new byte[32]; + for (int i = 0; i != x.length; i++) + { + x[i] = keyEnc[32 - 1 - i]; } - else + + for (int i = 0; i != y.length; i++) { - X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithm().getParameters()); - ECCurve curve; - EllipticCurve ellipticCurve; + y[i] = keyEnc[64 - 1 - i]; + } - if (params.isNamedCurve()) - { - ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters(); - X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid); - - curve = ecP.getCurve(); - ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed()); - - ecSpec = new ECNamedCurveSpec( - ECUtil.getCurveName(oid), - ellipticCurve, - new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), - ecP.getN(), - ecP.getH()); - } - else if (params.isImplicitlyCA()) - { - ecSpec = null; - curve = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa().getCurve(); - } - else - { - X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters()); - - curve = ecP.getCurve(); - ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed()); - - this.ecSpec = new ECParameterSpec( - ellipticCurve, - new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), - ecP.getN(), - ecP.getH().intValue()); - } + gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getAlgorithm().getParameters()); - DERBitString bits = info.getPublicKeyData(); - byte[] data = bits.getBytes(); - ASN1OctetString key = new DEROctetString(data); + ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet())); - // - // extra octet string - one of our old certs... - // - if (data[0] == 0x04 && data[1] == data.length - 2 - && (data[2] == 0x02 || data[2] == 0x03)) - { - int qLength = new X9IntegerConverter().getByteLength(curve); - - if (qLength >= data.length - 3) - { - try - { - key = (ASN1OctetString) ASN1Primitive.fromByteArray(data); - } - catch (IOException ex) - { - throw new IllegalArgumentException("error recovering public key"); - } - } - } - X9ECPoint derQ = new X9ECPoint(curve, key); + ECCurve curve = spec.getCurve(); + EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed()); - this.q = derQ.getPoint(); - } + this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y)); + + ecSpec = new ECNamedCurveSpec( + ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()), + ellipticCurve, + new ECPoint( + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), + spec.getN(), spec.getH()); } public String getAlgorithm() @@ -310,71 +227,23 @@ public class BCECGOST3410PublicKey public byte[] getEncoded() { - ASN1Encodable params; + ASN1Encodable params; SubjectPublicKeyInfo info; - if (algorithm.equals("ECGOST3410")) + if (gostParams != null) { - if (gostParams != null) - { - params = gostParams; - } - else - { - if (ecSpec instanceof ECNamedCurveSpec) - { - params = new GOST3410PublicKeyAlgParameters( - ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()), - CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet); - } - else - { // strictly speaking this may not be applicable... - ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); - - X9ECParameters ecP = new X9ECParameters( - curve, - EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression), - ecSpec.getOrder(), - BigInteger.valueOf(ecSpec.getCofactor()), - ecSpec.getCurve().getSeed()); - - params = new X962Parameters(ecP); - } - } - - BigInteger bX = this.q.getX().toBigInteger(); - BigInteger bY = this.q.getY().toBigInteger(); - byte[] encKey = new byte[64]; - - extractBytes(encKey, 0, bX); - extractBytes(encKey, 32, bY); - - try - { - info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), new DEROctetString(encKey)); - } - catch (IOException e) - { - return null; - } + params = gostParams; } else { if (ecSpec instanceof ECNamedCurveSpec) { - ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName()); - if (curveOid == null) - { - curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()); - } - params = new X962Parameters(curveOid); - } - else if (ecSpec == null) - { - params = new X962Parameters(DERNull.INSTANCE); + params = new GOST3410PublicKeyAlgParameters( + ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()), + CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet); } else - { + { // strictly speaking this may not be applicable... ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve()); X9ECParameters ecP = new X9ECParameters( @@ -386,12 +255,22 @@ public class BCECGOST3410PublicKey params = new X962Parameters(ecP); } + } + + BigInteger bX = this.q.getAffineXCoord().toBigInteger(); + BigInteger bY = this.q.getAffineYCoord().toBigInteger(); + byte[] encKey = new byte[64]; - ECCurve curve = this.engineGetQ().getCurve(); - ASN1OctetString p = (ASN1OctetString) - new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive(); + extractBytes(encKey, 0, bX); + extractBytes(encKey, 32, bY); - info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); + try + { + info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), new DEROctetString(encKey)); + } + catch (IOException e) + { + return null; } return KeyUtil.getEncodedSubjectPublicKeyInfo(info); @@ -430,7 +309,7 @@ public class BCECGOST3410PublicKey public ECPoint getW() { - return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger()); + return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); } public org.bouncycastle.math.ec.ECPoint getQ() @@ -439,11 +318,11 @@ public class BCECGOST3410PublicKey { if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp) { - return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY()); + return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord()); } else { - return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY()); + return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord()); } } @@ -467,19 +346,19 @@ public class BCECGOST3410PublicKey public String toString() { - StringBuffer buf = new StringBuffer(); - String nl = System.getProperty("line.separator"); + StringBuffer buf = new StringBuffer(); + String nl = System.getProperty("line.separator"); buf.append("EC Public Key").append(nl); - buf.append(" X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl); - buf.append(" Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl); + buf.append(" X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl); + buf.append(" Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl); return buf.toString(); } - + public void setPointFormat(String style) { - withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); + withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style)); } public boolean equals(Object o) @@ -518,4 +397,9 @@ public class BCECGOST3410PublicKey out.writeObject(this.getEncoded()); } + + public GOST3410PublicKeyAlgParameters getGostParams() + { + return gostParams; + } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java index ce0e603..a2114fa 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java @@ -1,6 +1,9 @@ package org.bouncycastle.jcajce.provider.asymmetric.rsa; import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OptionalDataException; import java.math.BigInteger; import java.security.interfaces.RSAPublicKey; import java.security.spec.RSAPublicKeySpec; @@ -15,14 +18,18 @@ import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil; public class BCRSAPublicKey implements RSAPublicKey { + private static final AlgorithmIdentifier DEFAULT_ALGORITHM_IDENTIFIER = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE); + static final long serialVersionUID = 2675817738516720772L; - + private BigInteger modulus; private BigInteger publicExponent; + private transient AlgorithmIdentifier algorithmIdentifier; BCRSAPublicKey( RSAKeyParameters key) { + this.algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER; this.modulus = key.getModulus(); this.publicExponent = key.getExponent(); } @@ -30,6 +37,7 @@ public class BCRSAPublicKey BCRSAPublicKey( RSAPublicKeySpec spec) { + this.algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER; this.modulus = spec.getModulus(); this.publicExponent = spec.getPublicExponent(); } @@ -37,6 +45,7 @@ public class BCRSAPublicKey BCRSAPublicKey( RSAPublicKey key) { + this.algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER; this.modulus = key.getModulus(); this.publicExponent = key.getPublicExponent(); } @@ -44,10 +53,16 @@ public class BCRSAPublicKey BCRSAPublicKey( SubjectPublicKeyInfo info) { + populateFromPublicKeyInfo(info); + } + + private void populateFromPublicKeyInfo(SubjectPublicKeyInfo info) + { try { org.bouncycastle.asn1.pkcs.RSAPublicKey pubKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(info.parsePublicKey()); + this.algorithmIdentifier = info.getAlgorithm(); this.modulus = pubKey.getModulus(); this.publicExponent = pubKey.getPublicExponent(); } @@ -89,7 +104,7 @@ public class BCRSAPublicKey public byte[] getEncoded() { - return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new org.bouncycastle.asn1.pkcs.RSAPublicKey(getModulus(), getPublicExponent())); + return KeyUtil.getEncodedSubjectPublicKeyInfo(algorithmIdentifier, new org.bouncycastle.asn1.pkcs.RSAPublicKey(getModulus(), getPublicExponent())); } public int hashCode() @@ -126,4 +141,32 @@ public class BCRSAPublicKey return buf.toString(); } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + try + { + algorithmIdentifier = AlgorithmIdentifier.getInstance(in.readObject()); + } + catch (OptionalDataException e) + { + algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER; + } + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + if (!algorithmIdentifier.equals(DEFAULT_ALGORITHM_IDENTIFIER)) + { + out.writeObject(algorithmIdentifier.getEncoded()); + } + } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java index d4065ac..5eea1b9 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java @@ -74,8 +74,8 @@ public class EC5Util ((ECNamedCurveParameterSpec)spec).getName(), ellipticCurve, new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), spec.getN(), spec.getH()); } @@ -84,8 +84,8 @@ public class EC5Util return new ECParameterSpec( ellipticCurve, new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), spec.getN(), spec.getH().intValue()); } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java index 1888328..32e595c 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java @@ -31,7 +31,7 @@ import org.bouncycastle.asn1.x509.X509Extension; * ReasonCode Hode Instruction Code Invalidity Date Certificate Issuer * (critical) */ -class X509CRLEntryObject extends X509CRLEntry +public class X509CRLEntryObject extends X509CRLEntry { private TBSCertList.CRLEntry c; @@ -39,7 +39,7 @@ class X509CRLEntryObject extends X509CRLEntry private int hashValue; private boolean isHashValueSet; - public X509CRLEntryObject(TBSCertList.CRLEntry c) + protected X509CRLEntryObject(TBSCertList.CRLEntry c) { this.c = c; this.certificateIssuer = null; @@ -62,7 +62,7 @@ class X509CRLEntryObject extends X509CRLEntry * @param previousCertificateIssuer * Certificate issuer of the previous CRLEntry. */ - public X509CRLEntryObject( + protected X509CRLEntryObject( TBSCertList.CRLEntry c, boolean isIndirect, X500Name previousCertificateIssuer) @@ -211,6 +211,23 @@ class X509CRLEntryObject extends X509CRLEntry return hashValue; } + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o instanceof X509CRLEntryObject) + { + X509CRLEntryObject other = (X509CRLEntryObject)o; + + return this.c.equals(other.c); + } + + return super.equals(this); + } + public byte[] getEncoded() throws CRLException { diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java index 2fc0826..c7d0402 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java @@ -54,13 +54,15 @@ import org.bouncycastle.util.encoders.Hex; * Delta CRL Indicator (critical) * Issuing Distribution Point (critical) */ -class X509CRLObject +public class X509CRLObject extends X509CRL { private CertificateList c; private String sigAlgName; private byte[] sigAlgParams; private boolean isIndirect; + private boolean isHashCodeSet = false; + private int hashCodeValue; static boolean isIndirectCRL(X509CRL crl) throws CRLException @@ -78,7 +80,7 @@ class X509CRLObject } } - public X509CRLObject( + protected X509CRLObject( CertificateList c) throws CRLException { @@ -522,19 +524,21 @@ class X509CRLObject throw new RuntimeException("X.509 CRL used with non X.509 Cert"); } - TBSCertList.CRLEntry[] certs = c.getRevokedCertificates(); + Enumeration certs = c.getRevokedCertificateEnumeration(); X500Name caName = c.getIssuer(); - if (certs != null) + if (certs.hasMoreElements()) { BigInteger serial = ((X509Certificate)cert).getSerialNumber(); - for (int i = 0; i < certs.length; i++) + while (certs.hasMoreElements()) { - if (isIndirect && certs[i].hasExtensions()) + TBSCertList.CRLEntry entry = TBSCertList.CRLEntry.getInstance(certs.nextElement()); + + if (isIndirect && entry.hasExtensions()) { - Extension currentCaName = certs[i].getExtensions().getExtension(Extension.certificateIssuer); + Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer); if (currentCaName != null) { @@ -542,7 +546,7 @@ class X509CRLObject } } - if (certs[i].getUserCertificate().getValue().equals(serial)) + if (entry.getUserCertificate().getValue().equals(serial)) { X500Name issuer; @@ -574,5 +578,50 @@ class X509CRLObject return false; } + + public boolean equals(Object other) + { + if (this == other) + { + return true; + } + + if (!(other instanceof X509CRL)) + { + return false; + } + + if (other instanceof X509CRLObject) + { + X509CRLObject crlObject = (X509CRLObject)other; + + if (isHashCodeSet) + { + boolean otherIsHashCodeSet = crlObject.isHashCodeSet; + if (otherIsHashCodeSet) + { + if (crlObject.hashCodeValue != hashCodeValue) + { + return false; + } + } + } + + return this.c.equals(crlObject.c); + } + + return super.equals(other); + } + + public int hashCode() + { + if (!isHashCodeSet) + { + isHashCodeSet = true; + hashCodeValue = super.hashCode(); + } + + return hashCodeValue; + } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java index 7ff57d3..2112673 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java @@ -7,6 +7,7 @@ import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; +import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory; public class GOST3411 { @@ -46,6 +47,18 @@ public class GOST3411 } } + /** + * PBEWithHmacGOST3411 + */ + public static class PBEWithMacKeyFactory + extends PBESecretKeyFactory + { + public PBEWithMacKeyFactory() + { + super("PBEwithHmacGOST3411", null, false, PKCS12, GOST3411, 256, 0); + } + } + public static class KeyGenerator extends BaseKeyGenerator { @@ -71,6 +84,9 @@ public class GOST3411 provider.addAlgorithm("Alg.Alias.MessageDigest.GOST-3411", "GOST3411"); provider.addAlgorithm("Alg.Alias.MessageDigest." + CryptoProObjectIdentifiers.gostR3411, "GOST3411"); + provider.addAlgorithm("SecretKeyFactory.PBEWITHHMACGOST3411", PREFIX + "$PBEWithMacKeyFactory"); + provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + CryptoProObjectIdentifiers.gostR3411, "PBEWITHHMACGOST3411"); + addHMACAlgorithm(provider, "GOST3411", PREFIX + "$HashMac", PREFIX + "$KeyGenerator"); addHMACAlias(provider, "GOST3411", CryptoProObjectIdentifiers.gostR3411); } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java index df5d41a..c7502c7 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java @@ -193,7 +193,6 @@ public class SHA1 provider.addAlgorithm("SecretKeyFactory.PBEWITHHMACSHA1", PREFIX + "$PBEWithMacKeyFactory"); provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA1", PREFIX + "$PBKDF2WithHmacSHA1UTF8"); - provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.id_PBKDF2, "PBKDF2WithHmacSHA1"); provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBKDF2WithHmacSHA1AndUTF8", "PBKDF2WithHmacSHA1"); provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA1And8BIT", PREFIX + "$PBKDF2WithHmacSHA18BIT"); } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java new file mode 100644 index 0000000..8050e35 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java @@ -0,0 +1,47 @@ +package org.bouncycastle.jcajce.provider.digest; + +import org.bouncycastle.crypto.digests.SM3Digest; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; + +public class SM3 +{ + private SM3() + { + } + + static public class Digest + extends BCMessageDigest + implements Cloneable + { + public Digest() + { + super(new SM3Digest()); + } + + public Object clone() + throws CloneNotSupportedException + { + Digest d = (Digest)super.clone(); + d.digest = new SM3Digest((SM3Digest)digest); + + return d; + } + } + + public static class Mappings + extends DigestAlgorithmProvider + { + private static final String PREFIX = SM3.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("MessageDigest.SM3", PREFIX + "$Digest"); + provider.addAlgorithm("Alg.Alias.MessageDigest.SM3", "SM3"); + provider.addAlgorithm("Alg.Alias.MessageDigest.1.2.156.197.1.401", "SM3"); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java new file mode 100644 index 0000000..1191049 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java @@ -0,0 +1,740 @@ +package org.bouncycastle.jcajce.provider.digest; + +import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.digests.SkeinDigest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.macs.SkeinMac; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; + +public class Skein +{ + private Skein() + { + } + + public static class DigestSkein256 + extends BCMessageDigest + implements Cloneable + { + public DigestSkein256(int outputSize) + { + super(new SkeinDigest(SkeinDigest.SKEIN_256, outputSize)); + } + + public Object clone() + throws CloneNotSupportedException + { + BCMessageDigest d = (BCMessageDigest)super.clone(); + d.digest = new SkeinDigest((SkeinDigest)digest); + + return d; + } + } + + public static class Digest_256_128 + extends DigestSkein256 + { + public Digest_256_128() + { + super(128); + } + } + + public static class Digest_256_160 + extends DigestSkein256 + { + public Digest_256_160() + { + super(160); + } + } + + public static class Digest_256_224 + extends DigestSkein256 + { + public Digest_256_224() + { + super(224); + } + } + + public static class Digest_256_256 + extends DigestSkein256 + { + public Digest_256_256() + { + super(256); + } + } + + public static class DigestSkein512 + extends BCMessageDigest + implements Cloneable + { + public DigestSkein512(int outputSize) + { + super(new SkeinDigest(SkeinDigest.SKEIN_512, outputSize)); + } + + public Object clone() + throws CloneNotSupportedException + { + BCMessageDigest d = (BCMessageDigest)super.clone(); + d.digest = new SkeinDigest((SkeinDigest)digest); + + return d; + } + } + + public static class Digest_512_128 + extends DigestSkein512 + { + public Digest_512_128() + { + super(128); + } + } + + public static class Digest_512_160 + extends DigestSkein512 + { + public Digest_512_160() + { + super(160); + } + } + + public static class Digest_512_224 + extends DigestSkein512 + { + public Digest_512_224() + { + super(224); + } + } + + public static class Digest_512_256 + extends DigestSkein512 + { + public Digest_512_256() + { + super(256); + } + } + + public static class Digest_512_384 + extends DigestSkein512 + { + public Digest_512_384() + { + super(384); + } + } + + public static class Digest_512_512 + extends DigestSkein512 + { + public Digest_512_512() + { + super(512); + } + } + + public static class DigestSkein1024 + extends BCMessageDigest + implements Cloneable + { + public DigestSkein1024(int outputSize) + { + super(new SkeinDigest(SkeinDigest.SKEIN_1024, outputSize)); + } + + public Object clone() + throws CloneNotSupportedException + { + BCMessageDigest d = (BCMessageDigest)super.clone(); + d.digest = new SkeinDigest((SkeinDigest)digest); + + return d; + } + } + + public static class Digest_1024_384 + extends DigestSkein1024 + { + public Digest_1024_384() + { + super(384); + } + } + + public static class Digest_1024_512 + extends DigestSkein1024 + { + public Digest_1024_512() + { + super(512); + } + } + + public static class Digest_1024_1024 + extends DigestSkein1024 + { + public Digest_1024_1024() + { + super(1024); + } + } + + /** + * Skein HMac + */ + public static class HashMac_256_128 + extends BaseMac + { + public HashMac_256_128() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 128))); + } + } + + public static class HashMac_256_160 + extends BaseMac + { + public HashMac_256_160() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 160))); + } + } + + public static class HashMac_256_224 + extends BaseMac + { + public HashMac_256_224() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 224))); + } + } + + public static class HashMac_256_256 + extends BaseMac + { + public HashMac_256_256() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 256))); + } + } + + public static class HashMac_512_128 + extends BaseMac + { + public HashMac_512_128() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 128))); + } + } + + public static class HashMac_512_160 + extends BaseMac + { + public HashMac_512_160() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 160))); + } + } + + public static class HashMac_512_224 + extends BaseMac + { + public HashMac_512_224() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 224))); + } + } + + public static class HashMac_512_256 + extends BaseMac + { + public HashMac_512_256() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 256))); + } + } + + public static class HashMac_512_384 + extends BaseMac + { + public HashMac_512_384() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 384))); + } + } + + public static class HashMac_512_512 + extends BaseMac + { + public HashMac_512_512() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 512))); + } + } + + public static class HashMac_1024_384 + extends BaseMac + { + public HashMac_1024_384() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_1024, 384))); + } + } + + public static class HashMac_1024_512 + extends BaseMac + { + public HashMac_1024_512() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_1024, 512))); + } + } + + public static class HashMac_1024_1024 + extends BaseMac + { + public HashMac_1024_1024() + { + super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_1024, 1024))); + } + } + + public static class HMacKeyGenerator_256_128 + extends BaseKeyGenerator + { + public HMacKeyGenerator_256_128() + { + super("HMACSkein-256-128", 128, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_256_160 + extends BaseKeyGenerator + { + public HMacKeyGenerator_256_160() + { + super("HMACSkein-256-160", 160, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_256_224 + extends BaseKeyGenerator + { + public HMacKeyGenerator_256_224() + { + super("HMACSkein-256-224", 224, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_256_256 + extends BaseKeyGenerator + { + public HMacKeyGenerator_256_256() + { + super("HMACSkein-256-256", 256, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_512_128 + extends BaseKeyGenerator + { + public HMacKeyGenerator_512_128() + { + super("HMACSkein-512-128", 128, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_512_160 + extends BaseKeyGenerator + { + public HMacKeyGenerator_512_160() + { + super("HMACSkein-512-160", 160, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_512_224 + extends BaseKeyGenerator + { + public HMacKeyGenerator_512_224() + { + super("HMACSkein-512-224", 224, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_512_256 + extends BaseKeyGenerator + { + public HMacKeyGenerator_512_256() + { + super("HMACSkein-512-256", 256, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_512_384 + extends BaseKeyGenerator + { + public HMacKeyGenerator_512_384() + { + super("HMACSkein-512-384", 384, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_512_512 + extends BaseKeyGenerator + { + public HMacKeyGenerator_512_512() + { + super("HMACSkein-512-512", 512, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_1024_384 + extends BaseKeyGenerator + { + public HMacKeyGenerator_1024_384() + { + super("HMACSkein-1024-384", 384, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_1024_512 + extends BaseKeyGenerator + { + public HMacKeyGenerator_1024_512() + { + super("HMACSkein-1024-512", 512, new CipherKeyGenerator()); + } + } + + public static class HMacKeyGenerator_1024_1024 + extends BaseKeyGenerator + { + public HMacKeyGenerator_1024_1024() + { + super("HMACSkein-1024-1024", 1024, new CipherKeyGenerator()); + } + } + + /* + * Skein-MAC + */ + public static class SkeinMac_256_128 + extends BaseMac + { + public SkeinMac_256_128() + { + super(new SkeinMac(SkeinMac.SKEIN_256, 128)); + } + } + + public static class SkeinMac_256_160 + extends BaseMac + { + public SkeinMac_256_160() + { + super(new SkeinMac(SkeinMac.SKEIN_256, 160)); + } + } + + public static class SkeinMac_256_224 + extends BaseMac + { + public SkeinMac_256_224() + { + super(new SkeinMac(SkeinMac.SKEIN_256, 224)); + } + } + + public static class SkeinMac_256_256 + extends BaseMac + { + public SkeinMac_256_256() + { + super(new SkeinMac(SkeinMac.SKEIN_256, 256)); + } + } + + public static class SkeinMac_512_128 + extends BaseMac + { + public SkeinMac_512_128() + { + super(new SkeinMac(SkeinMac.SKEIN_512, 128)); + } + } + + public static class SkeinMac_512_160 + extends BaseMac + { + public SkeinMac_512_160() + { + super(new SkeinMac(SkeinMac.SKEIN_512, 160)); + } + } + + public static class SkeinMac_512_224 + extends BaseMac + { + public SkeinMac_512_224() + { + super(new SkeinMac(SkeinMac.SKEIN_512, 224)); + } + } + + public static class SkeinMac_512_256 + extends BaseMac + { + public SkeinMac_512_256() + { + super(new SkeinMac(SkeinMac.SKEIN_512, 256)); + } + } + + public static class SkeinMac_512_384 + extends BaseMac + { + public SkeinMac_512_384() + { + super(new SkeinMac(SkeinMac.SKEIN_512, 384)); + } + } + + public static class SkeinMac_512_512 + extends BaseMac + { + public SkeinMac_512_512() + { + super(new SkeinMac(SkeinMac.SKEIN_512, 512)); + } + } + + public static class SkeinMac_1024_384 + extends BaseMac + { + public SkeinMac_1024_384() + { + super(new SkeinMac(SkeinMac.SKEIN_1024, 384)); + } + } + + public static class SkeinMac_1024_512 + extends BaseMac + { + public SkeinMac_1024_512() + { + super(new SkeinMac(SkeinMac.SKEIN_1024, 512)); + } + } + + public static class SkeinMac_1024_1024 + extends BaseMac + { + public SkeinMac_1024_1024() + { + super(new SkeinMac(SkeinMac.SKEIN_1024, 1024)); + } + } + + public static class SkeinMacKeyGenerator_256_128 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_256_128() + { + super("Skein-MAC-256-128", 128, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_256_160 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_256_160() + { + super("Skein-MAC-256-160", 160, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_256_224 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_256_224() + { + super("Skein-MAC-256-224", 224, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_256_256 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_256_256() + { + super("Skein-MAC-256-256", 256, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_512_128 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_512_128() + { + super("Skein-MAC-512-128", 128, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_512_160 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_512_160() + { + super("Skein-MAC-512-160", 160, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_512_224 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_512_224() + { + super("Skein-MAC-512-224", 224, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_512_256 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_512_256() + { + super("Skein-MAC-512-256", 256, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_512_384 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_512_384() + { + super("Skein-MAC-512-384", 384, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_512_512 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_512_512() + { + super("Skein-MAC-512-512", 512, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_1024_384 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_1024_384() + { + super("Skein-MAC-1024-384", 384, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_1024_512 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_1024_512() + { + super("Skein-MAC-1024-512", 512, new CipherKeyGenerator()); + } + } + + public static class SkeinMacKeyGenerator_1024_1024 + extends BaseKeyGenerator + { + public SkeinMacKeyGenerator_1024_1024() + { + super("Skein-MAC-1024-1024", 1024, new CipherKeyGenerator()); + } + } + + public static class Mappings + extends DigestAlgorithmProvider + { + private static final String PREFIX = Skein.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + // Skein sizes as used in "The Skein Hash Function Family 1.3" + + provider.addAlgorithm("MessageDigest.Skein-256-128", PREFIX + "$Digest_256_128"); + provider.addAlgorithm("MessageDigest.Skein-256-160", PREFIX + "$Digest_256_160"); + provider.addAlgorithm("MessageDigest.Skein-256-224", PREFIX + "$Digest_256_224"); + provider.addAlgorithm("MessageDigest.Skein-256-256", PREFIX + "$Digest_256_256"); + + provider.addAlgorithm("MessageDigest.Skein-512-128", PREFIX + "$Digest_512_128"); + provider.addAlgorithm("MessageDigest.Skein-512-160", PREFIX + "$Digest_512_160"); + provider.addAlgorithm("MessageDigest.Skein-512-224", PREFIX + "$Digest_512_224"); + provider.addAlgorithm("MessageDigest.Skein-512-256", PREFIX + "$Digest_512_256"); + provider.addAlgorithm("MessageDigest.Skein-512-384", PREFIX + "$Digest_512_384"); + provider.addAlgorithm("MessageDigest.Skein-512-512", PREFIX + "$Digest_512_512"); + + provider.addAlgorithm("MessageDigest.Skein-1024-384", PREFIX + "$Digest_1024_384"); + provider.addAlgorithm("MessageDigest.Skein-1024-512", PREFIX + "$Digest_1024_512"); + provider.addAlgorithm("MessageDigest.Skein-1024-1024", PREFIX + "$Digest_1024_1024"); + + addHMACAlgorithm(provider, "Skein-256-128", PREFIX + "$HashMac_256_128", PREFIX + "$HMacKeyGenerator_256_128"); + addHMACAlgorithm(provider, "Skein-256-160", PREFIX + "$HashMac_256_160", PREFIX + "$HMacKeyGenerator_256_160"); + addHMACAlgorithm(provider, "Skein-256-224", PREFIX + "$HashMac_256_224", PREFIX + "$HMacKeyGenerator_256_224"); + addHMACAlgorithm(provider, "Skein-256-256", PREFIX + "$HashMac_256_256", PREFIX + "$HMacKeyGenerator_256_256"); + + addHMACAlgorithm(provider, "Skein-512-128", PREFIX + "$HashMac_512_128", PREFIX + "$HMacKeyGenerator_512_128"); + addHMACAlgorithm(provider, "Skein-512-160", PREFIX + "$HashMac_512_160", PREFIX + "$HMacKeyGenerator_512_160"); + addHMACAlgorithm(provider, "Skein-512-224", PREFIX + "$HashMac_512_224", PREFIX + "$HMacKeyGenerator_512_224"); + addHMACAlgorithm(provider, "Skein-512-256", PREFIX + "$HashMac_512_256", PREFIX + "$HMacKeyGenerator_512_256"); + addHMACAlgorithm(provider, "Skein-512-384", PREFIX + "$HashMac_512_384", PREFIX + "$HMacKeyGenerator_512_384"); + addHMACAlgorithm(provider, "Skein-512-512", PREFIX + "$HashMac_512_512", PREFIX + "$HMacKeyGenerator_512_512"); + + addHMACAlgorithm(provider, "Skein-1024-384", PREFIX + "$HashMac_1024_384", PREFIX + "$HMacKeyGenerator_1024_384"); + addHMACAlgorithm(provider, "Skein-1024-512", PREFIX + "$HashMac_1024_512", PREFIX + "$HMacKeyGenerator_1024_512"); + addHMACAlgorithm(provider, "Skein-1024-1024", PREFIX + "$HashMac_1024_1024", PREFIX + "$HMacKeyGenerator_1024_1024"); + + addSkeinMacAlgorithm(provider, 256, 128); + addSkeinMacAlgorithm(provider, 256, 160); + addSkeinMacAlgorithm(provider, 256, 224); + addSkeinMacAlgorithm(provider, 256, 256); + + addSkeinMacAlgorithm(provider, 512, 128); + addSkeinMacAlgorithm(provider, 512, 160); + addSkeinMacAlgorithm(provider, 512, 224); + addSkeinMacAlgorithm(provider, 512, 256); + addSkeinMacAlgorithm(provider, 512, 384); + addSkeinMacAlgorithm(provider, 512, 512); + + addSkeinMacAlgorithm(provider, 1024, 384); + addSkeinMacAlgorithm(provider, 1024, 512); + addSkeinMacAlgorithm(provider, 1024, 1024); + } + + private void addSkeinMacAlgorithm(ConfigurableProvider provider, int blockSize, int outputSize) + { + String mainName = "Skein-MAC-" + blockSize + "-" + outputSize; + String algorithmClassName = PREFIX + "$SkeinMac_" + blockSize + "_" + outputSize; + String keyGeneratorClassName = PREFIX + "$SkeinMacKeyGenerator_" + blockSize + "_" + outputSize; + + provider.addAlgorithm("Mac." + mainName, algorithmClassName); + provider.addAlgorithm("Alg.Alias.Mac.Skein-MAC" + blockSize + "/" + outputSize, mainName); + provider.addAlgorithm("KeyGenerator." + mainName, keyGeneratorClassName); + provider.addAlgorithm("Alg.Alias.KeyGenerator.Skein-MAC" + blockSize + "/" + outputSize, mainName); + } + + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java index c255002..9a62c98 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java @@ -6,6 +6,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyStore; import java.security.KeyStore.LoadStoreParameter; @@ -24,13 +26,18 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.util.Collections; import java.util.Date; import java.util.Enumeration; +import java.util.HashMap; import java.util.Hashtable; +import java.util.Map; import java.util.Vector; import javax.crypto.Cipher; import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.IvParameterSpec; @@ -54,6 +61,10 @@ import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DEROutputStream; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.asn1.cryptopro.GOST28147Parameters; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.AuthenticatedSafe; import org.bouncycastle.asn1.pkcs.CertBag; import org.bouncycastle.asn1.pkcs.ContentInfo; @@ -75,12 +86,14 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.jcajce.provider.config.PKCS12StoreParameter; import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey; -import org.bouncycastle.jcajce.provider.util.SecretKeyUtil; +import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec; +import org.bouncycastle.jcajce.spec.PBKDF2KeySpec; import org.bouncycastle.jce.interfaces.BCKeyStore; import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.provider.JDKPKCS12StoreParameter; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Integers; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; @@ -92,6 +105,7 @@ public class PKCS12KeyStoreSpi private static final int MIN_ITERATIONS = 1024; private static final Provider bcProvider = new BouncyCastleProvider(); + private static final DefaultSecretKeyProvider keySizeProvider = new DefaultSecretKeyProvider(); private IgnoresCaseHashtable keys = new IgnoresCaseHashtable(); private Hashtable localIds = new Hashtable(); @@ -593,16 +607,8 @@ public class PKCS12KeyStoreSpi } else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2)) { - PBES2Parameters alg = PBES2Parameters.getInstance(algId.getParameters()); - PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters()); - SecretKeyFactory keyFact = SecretKeyFactory.getInstance(alg.getKeyDerivationFunc().getAlgorithm().getId(), bcProvider); - - SecretKey k = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), SecretKeyUtil.getKeySize(alg.getEncryptionScheme().getAlgorithm()))); - - Cipher cipher = Cipher.getInstance(alg.getEncryptionScheme().getAlgorithm().getId(), bcProvider); - - cipher.init(Cipher.UNWRAP_MODE, k, new IvParameterSpec(ASN1OctetString.getInstance(alg.getEncryptionScheme().getParameters()).getOctets())); + Cipher cipher = createCipher(Cipher.UNWRAP_MODE, password, algId); // we pass "" as the key algorithm type as it is unknown at this point return (PrivateKey)cipher.unwrap(data, "", Cipher.PRIVATE_KEY); @@ -656,29 +662,88 @@ public class PKCS12KeyStoreSpi byte[] data) throws IOException { - String algorithm = algId.getAlgorithm().getId(); - PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters()); - PBEKeySpec pbeSpec = new PBEKeySpec(password); + ASN1ObjectIdentifier algorithm = algId.getAlgorithm(); - try + if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds)) { - SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm, bcProvider); - PBEParameterSpec defParams = new PBEParameterSpec( - pbeParams.getIV(), - pbeParams.getIterations().intValue()); - BCPBEKey key = (BCPBEKey)keyFact.generateSecret(pbeSpec); + PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters()); + PBEKeySpec pbeSpec = new PBEKeySpec(password); - key.setTryWrongPKCS12Zero(wrongPKCS12Zero); + try + { + SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm.getId(), bcProvider); + PBEParameterSpec defParams = new PBEParameterSpec( + pbeParams.getIV(), + pbeParams.getIterations().intValue()); + BCPBEKey key = (BCPBEKey)keyFact.generateSecret(pbeSpec); - Cipher cipher = Cipher.getInstance(algorithm, bcProvider); - int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; - cipher.init(mode, key, defParams); - return cipher.doFinal(data); + key.setTryWrongPKCS12Zero(wrongPKCS12Zero); + + Cipher cipher = Cipher.getInstance(algorithm.getId(), bcProvider); + int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE; + cipher.init(mode, key, defParams); + return cipher.doFinal(data); + } + catch (Exception e) + { + throw new IOException("exception decrypting data - " + e.toString()); + } } - catch (Exception e) + else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2)) + { + try + { + Cipher cipher = createCipher(Cipher.DECRYPT_MODE, password, algId); + + return cipher.doFinal(data); + } + catch (Exception e) + { + throw new IOException("exception decrypting data - " + e.toString()); + } + } + else + { + throw new IOException("unknown PBE algorithm: " + algorithm); + } + } + + private Cipher createCipher(int mode, char[] password, AlgorithmIdentifier algId) + throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException + { + PBES2Parameters alg = PBES2Parameters.getInstance(algId.getParameters()); + PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters()); + AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme()); + + SecretKeyFactory keyFact = SecretKeyFactory.getInstance(alg.getKeyDerivationFunc().getAlgorithm().getId(), bcProvider); + SecretKey key; + + if (func.isDefaultPrf()) + { + key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme))); + } + else + { + key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf())); + } + + Cipher cipher = Cipher.getInstance(alg.getEncryptionScheme().getAlgorithm().getId()); + + AlgorithmIdentifier encryptionAlg = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme()); + + ASN1Encodable encParams = alg.getEncryptionScheme().getParameters(); + if (encParams instanceof ASN1OctetString) { - throw new IOException("exception decrypting data - " + e.toString()); + cipher.init(mode, key, new IvParameterSpec(ASN1OctetString.getInstance(encParams).getOctets())); } + else + { + // TODO: at the moment it's just GOST, but... + GOST28147Parameters gParams = GOST28147Parameters.getInstance(encParams); + + cipher.init(mode, key, new GOST28147ParameterSpec(gParams.getEncryptionParamSet(), gParams.getIV())); + } + return cipher; } public void engineLoad( @@ -1671,4 +1736,43 @@ public class PKCS12KeyStoreSpi return orig.elements(); } } + + private static class DefaultSecretKeyProvider + { + private final Map KEY_SIZES; + + DefaultSecretKeyProvider() + { + Map keySizes = new HashMap(); + + keySizes.put(new ASN1ObjectIdentifier("1.2.840.113533.7.66.10"), Integers.valueOf(128)); + + keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192)); + + keySizes.put(NISTObjectIdentifiers.id_aes128_CBC, Integers.valueOf(128)); + keySizes.put(NISTObjectIdentifiers.id_aes192_CBC, Integers.valueOf(192)); + keySizes.put(NISTObjectIdentifiers.id_aes256_CBC, Integers.valueOf(256)); + + keySizes.put(NTTObjectIdentifiers.id_camellia128_cbc, Integers.valueOf(128)); + keySizes.put(NTTObjectIdentifiers.id_camellia192_cbc, Integers.valueOf(192)); + keySizes.put(NTTObjectIdentifiers.id_camellia256_cbc, Integers.valueOf(256)); + + keySizes.put(CryptoProObjectIdentifiers.gostR28147_gcfb, Integers.valueOf(256)); + + KEY_SIZES = Collections.unmodifiableMap(keySizes); + } + + public int getKeySize(AlgorithmIdentifier algorithmIdentifier) + { + // TODO: not all ciphers/oid relationships are this simple. + Integer keySize = (Integer)KEY_SIZES.get(algorithmIdentifier.getAlgorithm()); + + if (keySize != null) + { + return keySize.intValue(); + } + + return -1; + } + } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java index 7a6f7b0..a600604 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java @@ -1,13 +1,18 @@ package org.bouncycastle.jcajce.provider.symmetric; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; import javax.crypto.spec.IvParameterSpec; import org.bouncycastle.asn1.bc.BCObjectIdentifiers; +import org.bouncycastle.asn1.cms.GCMParameters; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; @@ -15,6 +20,7 @@ import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.AESFastEngine; import org.bouncycastle.crypto.engines.AESWrapEngine; import org.bouncycastle.crypto.engines.RFC3211WrapEngine; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.macs.CMac; import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; @@ -23,6 +29,7 @@ import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.modes.OFBBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; @@ -31,9 +38,12 @@ import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Integers; public final class AES { + private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec"); + private AES() { } @@ -80,6 +90,15 @@ public final class AES } } + static public class GCM + extends BaseBlockCipher + { + public GCM() + { + super(new GCMBlockCipher(new AESFastEngine())); + } + } + public static class AESCMAC extends BaseMac { @@ -98,6 +117,24 @@ public final class AES } } + public static class Poly1305 + extends BaseMac + { + public Poly1305() + { + super(new org.bouncycastle.crypto.macs.Poly1305(new AESFastEngine())); + } + } + + public static class Poly1305KeyGen + extends BaseKeyGenerator + { + public Poly1305KeyGen() + { + super("Poly1305-AES", 256, new Poly1305KeyGenerator()); + } + } + static public class Wrap extends BaseWrapCipher { @@ -325,6 +362,95 @@ public final class AES } } + public static class AlgParamsGCM + extends BaseAlgorithmParameters + { + private GCMParameters gcmParams; + + protected void engineInit(AlgorithmParameterSpec paramSpec) + throws InvalidParameterSpecException + { + if (gcmSpecClass != null) + { + try + { + Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]); + Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]); + + + gcmParams = new GCMParameters((byte[])iv.invoke(paramSpec, new Object[0]), ((Integer)tLen.invoke(paramSpec, new Object[0])).intValue()); + } + catch (Exception e) + { + throw new InvalidParameterSpecException("Cannot process GCMParameterSpec."); + } + } + } + + protected void engineInit(byte[] params) + throws IOException + { + gcmParams = GCMParameters.getInstance(params); + } + + protected void engineInit(byte[] params, String format) + throws IOException + { + if (!isASN1FormatString(format)) + { + throw new IOException("unknown format specified"); + } + + gcmParams = GCMParameters.getInstance(params); + } + + protected byte[] engineGetEncoded() + throws IOException + { + return gcmParams.getEncoded(); + } + + protected byte[] engineGetEncoded(String format) + throws IOException + { + if (!isASN1FormatString(format)) + { + throw new IOException("unknown format specified"); + } + + return gcmParams.getEncoded(); + } + + protected String engineToString() + { + return "GCM"; + } + + protected AlgorithmParameterSpec localEngineGetParameterSpec(Class paramSpec) + throws InvalidParameterSpecException + { + if (gcmSpecClass != null) + { + try + { + Constructor constructor = gcmSpecClass.getConstructor(new Class[] { byte[].class, Integer.class }); + + return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { gcmParams.getNonce(), Integers.valueOf(gcmParams.getIcvLen()) }); + } + catch (NoSuchMethodException e) + { + throw new InvalidParameterSpecException("no constructor found!"); // should never happen + } + catch (Exception e) + { + throw new InvalidParameterSpecException("construction failed: " + e.getMessage()); // should never happen + } + } + + throw new InvalidParameterSpecException("unknown parameter spec: " + paramSpec.getName()); + } + } + public static class Mappings extends SymmetricAlgorithmProvider { @@ -353,6 +479,11 @@ public final class AES provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes192_CBC, "AES"); provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes256_CBC, "AES"); + provider.addAlgorithm("AlgorithmParameters.GCM", PREFIX + "$AlgParamsGCM"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes128_GCM, "GCM"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes192_GCM, "GCM"); + provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes256_GCM, "GCM"); + provider.addAlgorithm("AlgorithmParameterGenerator.AES", PREFIX + "$AlgParamGen"); provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + wrongAES128, "AES"); provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + wrongAES192, "AES"); @@ -383,6 +514,11 @@ public final class AES provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_wrap, "AESWRAP"); provider.addAlgorithm("Cipher.AESRFC3211WRAP", PREFIX + "$RFC3211Wrap"); + provider.addAlgorithm("Cipher.GCM", PREFIX + "$GCM"); + provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_GCM, "GCM"); + provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_GCM, "GCM"); + provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_GCM, "GCM"); + provider.addAlgorithm("KeyGenerator.AES", PREFIX + "$KeyGen"); provider.addAlgorithm("KeyGenerator." + wrongAES128, PREFIX + "$KeyGen128"); provider.addAlgorithm("KeyGenerator." + wrongAES192, PREFIX + "$KeyGen192"); @@ -484,6 +620,21 @@ public final class AES provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PKCS12PBE"); addGMacAlgorithm(provider, "AES", PREFIX + "$AESGMAC", PREFIX + "$KeyGen128"); + addPoly1305Algorithm(provider, "AES", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen"); + } + } + + private static Class lookup(String className) + { + try + { + Class def = AES.class.getClassLoader().loadClass(className); + + return def; + } + catch (Exception e) + { + return null; } } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java index 68605f4..d16e6c7 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java @@ -1,13 +1,16 @@ package org.bouncycastle.jcajce.provider.symmetric; +import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.CAST6Engine; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; +import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; public final class CAST6 { @@ -20,7 +23,13 @@ public final class CAST6 { public ECB() { - super(new CAST6Engine()); + super(new BlockCipherProvider() + { + public BlockCipher get() + { + return new CAST6Engine(); + } + }); } } @@ -42,6 +51,24 @@ public final class CAST6 } } + public static class Poly1305 + extends BaseMac + { + public Poly1305() + { + super(new org.bouncycastle.crypto.macs.Poly1305(new CAST6Engine())); + } + } + + public static class Poly1305KeyGen + extends BaseKeyGenerator + { + public Poly1305KeyGen() + { + super("Poly1305-CAST6", 256, new Poly1305KeyGenerator()); + } + } + public static class Mappings extends SymmetricAlgorithmProvider { @@ -57,6 +84,7 @@ public final class CAST6 provider.addAlgorithm("KeyGenerator.CAST6", PREFIX + "$KeyGen"); addGMacAlgorithm(provider, "CAST6", PREFIX + "$GMAC", PREFIX + "$KeyGen"); + addPoly1305Algorithm(provider, "CAST6", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen"); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java index 38b5ca7..95b5156 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java @@ -13,6 +13,7 @@ import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.CamelliaEngine; import org.bouncycastle.crypto.engines.CamelliaWrapEngine; import org.bouncycastle.crypto.engines.RFC3211WrapEngine; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; @@ -83,6 +84,24 @@ public final class Camellia } } + public static class Poly1305 + extends BaseMac + { + public Poly1305() + { + super(new org.bouncycastle.crypto.macs.Poly1305(new CamelliaEngine())); + } + } + + public static class Poly1305KeyGen + extends BaseKeyGenerator + { + public Poly1305KeyGen() + { + super("Poly1305-Camellia", 256, new Poly1305KeyGenerator()); + } + } + public static class KeyGen extends BaseKeyGenerator { @@ -213,6 +232,7 @@ public final class Camellia provider.addAlgorithm("KeyGenerator." + NTTObjectIdentifiers.id_camellia256_cbc, PREFIX + "$KeyGen256"); addGMacAlgorithm(provider, "CAMELLIA", PREFIX + "$GMAC", PREFIX + "$KeyGen"); + addPoly1305Algorithm(provider, "CAMELLIA", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen"); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java new file mode 100644 index 0000000..ff748ae --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java @@ -0,0 +1,51 @@ +package org.bouncycastle.jcajce.provider.symmetric; + +import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.engines.ChaChaEngine; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher; +import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; + +public final class ChaCha +{ + private ChaCha() + { + } + + public static class Base + extends BaseStreamCipher + { + public Base() + { + super(new ChaChaEngine(), 8); + } + } + + public static class KeyGen + extends BaseKeyGenerator + { + public KeyGen() + { + super("ChaCha", 128, new CipherKeyGenerator()); + } + } + + public static class Mappings + extends AlgorithmProvider + { + private static final String PREFIX = ChaCha.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + + provider.addAlgorithm("Cipher.CHACHA", PREFIX + "$Base"); + provider.addAlgorithm("KeyGenerator.CHACHA", PREFIX + "$KeyGen"); + + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java index 389b79a..b3ff96b 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java @@ -8,10 +8,12 @@ import java.security.spec.AlgorithmParameterSpec; import javax.crypto.spec.IvParameterSpec; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.GOST28147Engine; import org.bouncycastle.crypto.macs.GOST28147Mac; import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.modes.GCFBBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; @@ -45,6 +47,15 @@ public final class GOST28147 } } + public static class GCFB + extends BaseBlockCipher + { + public GCFB() + { + super(new BufferedBlockCipher(new GCFBBlockCipher(new GOST28147Engine())), 64); + } + } + /** * GOST28147 */ @@ -132,12 +143,12 @@ public final class GOST28147 provider.addAlgorithm("Cipher.GOST28147", PREFIX + "$ECB"); provider.addAlgorithm("Alg.Alias.Cipher.GOST", "GOST28147"); provider.addAlgorithm("Alg.Alias.Cipher.GOST-28147", "GOST28147"); - provider.addAlgorithm("Cipher." + CryptoProObjectIdentifiers.gostR28147_cbc, PREFIX + "$CBC"); + provider.addAlgorithm("Cipher." + CryptoProObjectIdentifiers.gostR28147_gcfb, PREFIX + "$GCFB"); provider.addAlgorithm("KeyGenerator.GOST28147", PREFIX + "$KeyGen"); provider.addAlgorithm("Alg.Alias.KeyGenerator.GOST", "GOST28147"); provider.addAlgorithm("Alg.Alias.KeyGenerator.GOST-28147", "GOST28147"); - provider.addAlgorithm("Alg.Alias.KeyGenerator." + CryptoProObjectIdentifiers.gostR28147_cbc, "GOST28147"); + provider.addAlgorithm("Alg.Alias.KeyGenerator." + CryptoProObjectIdentifiers.gostR28147_gcfb, "GOST28147"); provider.addAlgorithm("Mac.GOST28147MAC", PREFIX + "$Mac"); provider.addAlgorithm("Alg.Alias.Mac.GOST28147", "GOST28147MAC"); diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java index 2d089cc..a92f21d 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java @@ -7,8 +7,10 @@ import java.security.spec.AlgorithmParameterSpec; import javax.crypto.spec.IvParameterSpec; +import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.NoekeonEngine; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; @@ -16,6 +18,7 @@ import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGen import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac; +import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -30,7 +33,13 @@ public final class Noekeon { public ECB() { - super(new NoekeonEngine()); + super(new BlockCipherProvider() + { + public BlockCipher get() + { + return new NoekeonEngine(); + } + }); } } @@ -52,6 +61,24 @@ public final class Noekeon } } + public static class Poly1305 + extends BaseMac + { + public Poly1305() + { + super(new org.bouncycastle.crypto.macs.Poly1305(new NoekeonEngine())); + } + } + + public static class Poly1305KeyGen + extends BaseKeyGenerator + { + public Poly1305KeyGen() + { + super("Poly1305-Noekeon", 256, new Poly1305KeyGenerator()); + } + } + public static class AlgParamGen extends BaseAlgorithmParameterGenerator { @@ -120,6 +147,7 @@ public final class Noekeon provider.addAlgorithm("KeyGenerator.NOEKEON", PREFIX + "$KeyGen"); addGMacAlgorithm(provider, "NOEKEON", PREFIX + "$GMAC", PREFIX + "$KeyGen"); + addPoly1305Algorithm(provider, "NOEKEON", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen"); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java index ee3cac9..4b0d8b9 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java @@ -2,17 +2,28 @@ package org.bouncycastle.jcajce.provider.symmetric; import java.io.IOException; import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidParameterSpecException; +import java.security.spec.KeySpec; +import javax.crypto.SecretKey; +import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PBKDF2Params; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey; import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory; +import org.bouncycastle.jcajce.provider.symmetric.util.PBE; import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; +import org.bouncycastle.jcajce.spec.PBKDF2KeySpec; public class PBEPBKDF2 { @@ -104,6 +115,99 @@ public class PBEPBKDF2 } } + public static class BasePBKDF2 + extends BaseSecretKeyFactory + { + private int scheme; + + public BasePBKDF2(String name, int scheme) + { + super(name, PKCSObjectIdentifiers.id_PBKDF2); + + this.scheme = scheme; + } + + protected SecretKey engineGenerateSecret( + KeySpec keySpec) + throws InvalidKeySpecException + { + if (keySpec instanceof PBEKeySpec) + { + PBEKeySpec pbeSpec = (PBEKeySpec)keySpec; + + if (pbeSpec.getSalt() == null) + { + throw new InvalidKeySpecException("missing required salt"); + } + + if (pbeSpec.getIterationCount() <= 0) + { + throw new InvalidKeySpecException("positive iteration count required: " + + pbeSpec.getIterationCount()); + } + + if (pbeSpec.getKeyLength() <= 0) + { + throw new InvalidKeySpecException("positive key length required: " + + pbeSpec.getKeyLength()); + } + + if (pbeSpec.getPassword().length == 0) + { + throw new IllegalArgumentException("password empty"); + } + + if (pbeSpec instanceof PBKDF2KeySpec) + { + PBKDF2KeySpec spec = (PBKDF2KeySpec)pbeSpec; + + int digest = getDigestCode(spec.getPrf().getAlgorithm()); + int keySize = pbeSpec.getKeyLength(); + int ivSize = -1; // JDK 1,2 and earlier does not understand simplified version. + CipherParameters param = PBE.Util.makePBEMacParameters(pbeSpec, scheme, digest, keySize); + + return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, param); + } + else + { + int digest = SHA1; + int keySize = pbeSpec.getKeyLength(); + int ivSize = -1; // JDK 1,2 and earlier does not understand simplified version. + CipherParameters param = PBE.Util.makePBEMacParameters(pbeSpec, scheme, digest, keySize); + + return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, param); + } + } + + throw new InvalidKeySpecException("Invalid KeySpec"); + } + + + private int getDigestCode(ASN1ObjectIdentifier algorithm) + throws InvalidKeySpecException + { + if (algorithm.equals(CryptoProObjectIdentifiers.gostR3411Hmac)) + { + return GOST3411; + } + else if (algorithm.equals(PKCSObjectIdentifiers.id_hmacWithSHA1)) + { + return SHA1; + } + + throw new InvalidKeySpecException("Invalid KeySpec: unknown PRF algorithm " + algorithm); + } + } + + public static class PBKDF2withUTF8 + extends BasePBKDF2 + { + public PBKDF2withUTF8() + { + super("PBKDF2", PKCS5S2_UTF8); + } + } + public static class Mappings extends AlgorithmProvider { @@ -117,6 +221,8 @@ public class PBEPBKDF2 { provider.addAlgorithm("AlgorithmParameters.PBKDF2", PREFIX + "$AlgParams"); provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.id_PBKDF2, "PBKDF2"); + provider.addAlgorithm("SecretKeyFactory.PBKDF2", PREFIX + "$PBKDF2withUTF8"); + provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.id_PBKDF2, "PBKDF2"); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java index a29e717..114c40b 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java @@ -11,6 +11,7 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.RC6Engine; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.CFBBlockCipher; @@ -82,6 +83,24 @@ public final class RC6 } } + public static class Poly1305 + extends BaseMac + { + public Poly1305() + { + super(new org.bouncycastle.crypto.macs.Poly1305(new RC6Engine())); + } + } + + public static class Poly1305KeyGen + extends BaseKeyGenerator + { + public Poly1305KeyGen() + { + super("Poly1305-RC6", 256, new Poly1305KeyGenerator()); + } + } + public static class KeyGen extends BaseKeyGenerator { @@ -155,6 +174,7 @@ public final class RC6 provider.addAlgorithm("AlgorithmParameters.RC6", PREFIX + "$AlgParams"); addGMacAlgorithm(provider, "RC6", PREFIX + "$GMAC", PREFIX + "$KeyGen"); + addPoly1305Algorithm(provider, "RC6", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen"); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java index 2ad41bf..e7e257c 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java @@ -12,6 +12,7 @@ import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.SEEDEngine; import org.bouncycastle.crypto.engines.SEEDWrapEngine; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; @@ -82,6 +83,24 @@ public final class SEED } } + public static class Poly1305 + extends BaseMac + { + public Poly1305() + { + super(new org.bouncycastle.crypto.macs.Poly1305(new SEEDEngine())); + } + } + + public static class Poly1305KeyGen + extends BaseKeyGenerator + { + public Poly1305KeyGen() + { + super("Poly1305-SEED", 256, new Poly1305KeyGenerator()); + } + } + public static class AlgParamGen extends BaseAlgorithmParameterGenerator { @@ -158,6 +177,7 @@ public final class SEED provider.addAlgorithm("KeyGenerator." + KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, PREFIX + "$KeyGen"); addGMacAlgorithm(provider, "SEED", PREFIX + "$GMAC", PREFIX + "$KeyGen"); + addPoly1305Algorithm(provider, "SEED", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen"); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java index 578de32..ec21880 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java @@ -3,6 +3,8 @@ package org.bouncycastle.jcajce.provider.symmetric; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.SerpentEngine; +import org.bouncycastle.crypto.engines.TwofishEngine; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; @@ -51,6 +53,24 @@ public final class Serpent } } + public static class Poly1305 + extends BaseMac + { + public Poly1305() + { + super(new org.bouncycastle.crypto.macs.Poly1305(new TwofishEngine())); + } + } + + public static class Poly1305KeyGen + extends BaseKeyGenerator + { + public Poly1305KeyGen() + { + super("Poly1305-Serpent", 256, new Poly1305KeyGenerator()); + } + } + public static class AlgParams extends IvAlgorithmParameters { @@ -77,6 +97,7 @@ public final class Serpent provider.addAlgorithm("AlgorithmParameters.Serpent", PREFIX + "$AlgParams"); addGMacAlgorithm(provider, "SERPENT", PREFIX + "$SerpentGMAC", PREFIX + "$KeyGen"); + addPoly1305Algorithm(provider, "SERPENT", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen"); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java new file mode 100644 index 0000000..81666af --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java @@ -0,0 +1,124 @@ +package org.bouncycastle.jcajce.provider.symmetric; + +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.spec.IvParameterSpec; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.engines.Shacal2Engine; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public final class Shacal2 +{ + private Shacal2() + { + } + + public static class ECB + extends BaseBlockCipher + { + public ECB() + { + super(new BlockCipherProvider() + { + public BlockCipher get() + { + return new Shacal2Engine(); + } + }); + } + } + + public static class CBC + extends BaseBlockCipher + { + public CBC() + { + super(new CBCBlockCipher(new Shacal2Engine()), 256);//block size + } + } + + public static class KeyGen + extends BaseKeyGenerator + { + public KeyGen() + { + super("Shacal2", 512, new CipherKeyGenerator());//key size + } + } + + public static class AlgParamGen + extends BaseAlgorithmParameterGenerator + { + protected void engineInit( + AlgorithmParameterSpec genParamSpec, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for Shacal2 parameter generation."); + } + + protected AlgorithmParameters engineGenerateParameters() + { + byte[] iv = new byte[32];// block size 256 + + if (random == null) + { + random = new SecureRandom(); + } + + random.nextBytes(iv); + + AlgorithmParameters params; + + try + { + params = AlgorithmParameters.getInstance("Shacal2", BouncyCastleProvider.PROVIDER_NAME); + params.init(new IvParameterSpec(iv)); + } + catch (Exception e) + { + throw new RuntimeException(e.getMessage()); + } + return params; + } + } + + public static class AlgParams + extends IvAlgorithmParameters + { + protected String engineToString() + { + return "Shacal2 IV"; + } + } + + public static class Mappings + extends SymmetricAlgorithmProvider + { + private static final String PREFIX = Shacal2.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("Cipher.Shacal2", PREFIX + "$ECB"); + provider.addAlgorithm("KeyGenerator.Shacal2", PREFIX + "$KeyGen"); + provider.addAlgorithm("AlgorithmParameterGenerator.Shacal2", PREFIX + "$AlgParamGen"); + provider.addAlgorithm("AlgorithmParameters.Shacal2", PREFIX + "$AlgParams"); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java index 49656c2..c1b3d19 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java @@ -18,4 +18,17 @@ abstract class SymmetricAlgorithmProvider provider.addAlgorithm("KeyGenerator." + algorithm + "-GMAC", keyGeneratorClassName); provider.addAlgorithm("Alg.Alias.KeyGenerator." + algorithm + "GMAC", algorithm + "-GMAC"); } + + protected void addPoly1305Algorithm(ConfigurableProvider provider, + String algorithm, + String algorithmClassName, + String keyGeneratorClassName) + { + provider.addAlgorithm("Mac.POLY1305-" + algorithm, algorithmClassName); + provider.addAlgorithm("Alg.Alias.Mac.POLY1305" + algorithm, "POLY1305-" + algorithm); + + provider.addAlgorithm("KeyGenerator.POLY1305-" + algorithm, keyGeneratorClassName); + provider.addAlgorithm("Alg.Alias.KeyGenerator.POLY1305" + algorithm, "POLY1305-" + algorithm); + } + } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java new file mode 100644 index 0000000..2970de6 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java @@ -0,0 +1,120 @@ +package org.bouncycastle.jcajce.provider.symmetric; + +import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.engines.ThreefishEngine; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters; +import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; + +public final class Threefish +{ + private Threefish() + { + } + + public static class ECB_256 + extends BaseBlockCipher + { + public ECB_256() + { + super(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256)); + } + } + + public static class ECB_512 + extends BaseBlockCipher + { + public ECB_512() + { + super(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512)); + } + } + + public static class ECB_1024 + extends BaseBlockCipher + { + public ECB_1024() + { + super(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024)); + } + } + + public static class KeyGen_256 + extends BaseKeyGenerator + { + public KeyGen_256() + { + super("Threefish-256", 256, new CipherKeyGenerator()); + } + } + + public static class KeyGen_512 + extends BaseKeyGenerator + { + public KeyGen_512() + { + super("Threefish-512", 512, new CipherKeyGenerator()); + } + } + + public static class KeyGen_1024 + extends BaseKeyGenerator + { + public KeyGen_1024() + { + super("Threefish-1024", 1024, new CipherKeyGenerator()); + } + } + + public static class AlgParams_256 + extends IvAlgorithmParameters + { + protected String engineToString() + { + return "Threefish-256 IV"; + } + } + + public static class AlgParams_512 + extends IvAlgorithmParameters + { + protected String engineToString() + { + return "Threefish-512 IV"; + } + } + + public static class AlgParams_1024 + extends IvAlgorithmParameters + { + protected String engineToString() + { + return "Threefish-1024 IV"; + } + } + + public static class Mappings + extends AlgorithmProvider + { + private static final String PREFIX = Threefish.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("Cipher.Threefish-256", PREFIX + "$ECB_256"); + provider.addAlgorithm("Cipher.Threefish-512", PREFIX + "$ECB_512"); + provider.addAlgorithm("Cipher.Threefish-1024", PREFIX + "$ECB_1024"); + provider.addAlgorithm("KeyGenerator.Threefish-256", PREFIX + "$KeyGen_256"); + provider.addAlgorithm("KeyGenerator.Threefish-512", PREFIX + "$KeyGen_512"); + provider.addAlgorithm("KeyGenerator.Threefish-1024", PREFIX + "$KeyGen_1024"); + provider.addAlgorithm("AlgorithmParameters.Threefish-256", PREFIX + "$AlgParams_256"); + provider.addAlgorithm("AlgorithmParameters.Threefish-512", PREFIX + "$AlgParams_512"); + provider.addAlgorithm("AlgorithmParameters.Threefish-1024", PREFIX + "$AlgParams_1024"); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java index 67b9f66..4c3ab1c 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java @@ -3,6 +3,7 @@ package org.bouncycastle.jcajce.provider.symmetric; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.engines.TwofishEngine; +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; import org.bouncycastle.crypto.macs.GMac; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; @@ -53,6 +54,24 @@ public final class Twofish } } + public static class Poly1305 + extends BaseMac + { + public Poly1305() + { + super(new org.bouncycastle.crypto.macs.Poly1305(new TwofishEngine())); + } + } + + public static class Poly1305KeyGen + extends BaseKeyGenerator + { + public Poly1305KeyGen() + { + super("Poly1305-Twofish", 256, new Poly1305KeyGenerator()); + } + } + /** * PBEWithSHAAndTwofish-CBC */ @@ -107,6 +126,7 @@ public final class Twofish provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAANDTWOFISH-CBC", PREFIX + "$PBEWithSHAKeyFactory"); addGMacAlgorithm(provider, "Twofish", PREFIX + "$GMAC", PREFIX + "$KeyGen"); + addPoly1305Algorithm(provider, "Twofish", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen"); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java new file mode 100644 index 0000000..5be0640 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java @@ -0,0 +1,51 @@ +package org.bouncycastle.jcajce.provider.symmetric; + +import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.engines.XSalsa20Engine; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator; +import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher; +import org.bouncycastle.jcajce.provider.util.AlgorithmProvider; + +public final class XSalsa20 +{ + private XSalsa20() + { + } + + public static class Base + extends BaseStreamCipher + { + public Base() + { + super(new XSalsa20Engine(), 24); + } + } + + public static class KeyGen + extends BaseKeyGenerator + { + public KeyGen() + { + super("XSalsa20", 256, new CipherKeyGenerator()); + } + } + + public static class Mappings + extends AlgorithmProvider + { + private static final String PREFIX = XSalsa20.class.getName(); + + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + + provider.addAlgorithm("Cipher.XSALSA20", PREFIX + "$Base"); + provider.addAlgorithm("KeyGenerator.XSALSA20", PREFIX + "$KeyGen"); + + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index 17b66a5..943fa18 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -1,5 +1,7 @@ package org.bouncycastle.jcajce.provider.symmetric.util; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -20,6 +22,7 @@ import javax.crypto.spec.PBEParameterSpec; import javax.crypto.spec.RC2ParameterSpec; import javax.crypto.spec.RC5ParameterSpec; +import org.bouncycastle.asn1.cms.GCMParameters; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherParameters; @@ -32,6 +35,7 @@ import org.bouncycastle.crypto.modes.CCMBlockCipher; import org.bouncycastle.crypto.modes.CFBBlockCipher; import org.bouncycastle.crypto.modes.CTSBlockCipher; import org.bouncycastle.crypto.modes.EAXBlockCipher; +import org.bouncycastle.crypto.modes.GCFBBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.modes.GOFBBlockCipher; import org.bouncycastle.crypto.modes.OCBBlockCipher; @@ -46,21 +50,24 @@ import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.paddings.TBCPadding; import org.bouncycastle.crypto.paddings.X923Padding; import org.bouncycastle.crypto.paddings.ZeroBytePadding; +import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.params.ParametersWithSBox; import org.bouncycastle.crypto.params.RC2Parameters; import org.bouncycastle.crypto.params.RC5Parameters; +import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec; +import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.GOST28147ParameterSpec; -import org.bouncycastle.jce.spec.RepeatedSecretKeySpec; import org.bouncycastle.util.Strings; public class BaseBlockCipher extends BaseWrapCipher implements PBE { + private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec"); + // // specs we can handle. // @@ -70,13 +77,15 @@ public class BaseBlockCipher RC5ParameterSpec.class, IvParameterSpec.class, PBEParameterSpec.class, - GOST28147ParameterSpec.class + GOST28147ParameterSpec.class, + gcmSpecClass }; private BlockCipher baseEngine; private BlockCipherProvider engineProvider; private GenericBlockCipher cipher; private ParametersWithIV ivParam; + private AEADParameters aeadParams; private int ivLength = 0; @@ -87,6 +96,20 @@ public class BaseBlockCipher private String modeName = null; + private static Class lookup(String className) + { + try + { + Class def = BaseBlockCipher.class.getClassLoader().loadClass(className); + + return def; + } + catch (Exception e) + { + return null; + } + } + protected BaseBlockCipher( BlockCipher engine) { @@ -105,6 +128,14 @@ public class BaseBlockCipher } protected BaseBlockCipher( + AEADBlockCipher engine) + { + baseEngine = engine.getUnderlyingCipher(); + ivLength = baseEngine.getBlockSize(); + cipher = new AEADGenericBlockCipher(engine); + } + + protected BaseBlockCipher( org.bouncycastle.crypto.BlockCipher engine, int ivLength) { @@ -181,6 +212,18 @@ public class BaseBlockCipher throw new RuntimeException(e.toString()); } } + else if (aeadParams != null) + { + try + { + engineParams = AlgorithmParameters.getInstance("GCM", BouncyCastleProvider.PROVIDER_NAME); + engineParams.init(new GCMParameters(aeadParams.getNonce(), aeadParams.getMacSize()).getEncoded()); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } } return engineParams; @@ -271,6 +314,12 @@ public class BaseBlockCipher cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher( new GOFBBlockCipher(baseEngine))); } + else if (modeName.startsWith("GCFB")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher( + new GCFBBlockCipher(baseEngine))); + } else if (modeName.startsWith("CTS")) { ivLength = baseEngine.getBlockSize(); @@ -278,14 +327,15 @@ public class BaseBlockCipher } else if (modeName.startsWith("CCM")) { - ivLength = baseEngine.getBlockSize(); + ivLength = 13; // CCM nonce 7..13 bytes cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine)); } else if (modeName.startsWith("OCB")) { if (engineProvider != null) { - ivLength = baseEngine.getBlockSize(); + // Nonce restricted to max 120 bits over 128 bit block cipher since draft-irtf-cfrg-ocb-03 + ivLength = 15; cipher = new AEADGenericBlockCipher(new OCBBlockCipher(baseEngine, engineProvider.get())); } else @@ -377,6 +427,7 @@ public class BaseBlockCipher this.pbeSpec = null; this.pbeAlgorithm = null; this.engineParams = null; + this.aeadParams = null; // // basic key check @@ -419,6 +470,18 @@ public class BaseBlockCipher param = new ParametersWithIV(param, iv.getIV()); } + else if (params instanceof GOST28147ParameterSpec) + { + // need to pick up IV and SBox. + GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params; + + param = new ParametersWithSBox(param, gost28147Param.getSbox()); + + if (gost28147Param.getIV() != null && ivLength != 0) + { + param = new ParametersWithIV(param, gost28147Param.getIV()); + } + } } else if (params instanceof PBEParameterSpec) { @@ -528,12 +591,38 @@ public class BaseBlockCipher ivParam = (ParametersWithIV)param; } } + else if (gcmSpecClass != null && gcmSpecClass.isInstance(params)) + { + if (!isAEADModeName(modeName) && !(cipher instanceof AEADGenericBlockCipher)) + { + throw new InvalidAlgorithmParameterException("GCMParameterSpec can only be used with AEAD modes."); + } + + try + { + Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]); + Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]); + + if (key instanceof RepeatedSecretKeySpec) + { + param = aeadParams = new AEADParameters(null, ((Integer)tLen.invoke(params, new Object[0])).intValue(), (byte[])iv.invoke(params, new Object[0])); + } + else + { + param = aeadParams = new AEADParameters(new KeyParameter(key.getEncoded()), ((Integer)tLen.invoke(params, new Object[0])).intValue(), (byte[])iv.invoke(params, new Object[0])); + } + } + catch (Exception e) + { + throw new InvalidAlgorithmParameterException("Cannot process GCMParameterSpec."); + } + } else { throw new InvalidAlgorithmParameterException("unknown parameter type."); } - if ((ivLength != 0) && !(param instanceof ParametersWithIV)) + if ((ivLength != 0) && !(param instanceof ParametersWithIV) && !(param instanceof AEADParameters)) { SecureRandom ivRandom = random; @@ -596,6 +685,11 @@ public class BaseBlockCipher { for (int i = 0; i != availableSpecs.length; i++) { + if (availableSpecs[i] == null) + { + continue; + } + try { paramSpec = params.getParameterSpec(availableSpecs[i]); @@ -634,6 +728,18 @@ public class BaseBlockCipher } } + protected void engineUpdateAAD(byte[] input, int offset, int length) + { + cipher.updateAAD(input, offset, length); + } + + protected void engineUpdateAAD(ByteBuffer bytebuffer) + { + int offset = bytebuffer.arrayOffset() + bytebuffer.position(); + int length = bytebuffer.limit() - bytebuffer.position(); + engineUpdateAAD(bytebuffer.array(), offset, length); + } + protected byte[] engineUpdate( byte[] input, int inputOffset, @@ -783,6 +889,8 @@ public class BaseBlockCipher public int getUpdateOutputSize(int len); + public void updateAAD(byte[] input, int offset, int length); + public int processByte(byte in, byte[] out, int outOff) throws DataLengthException; @@ -844,6 +952,11 @@ public class BaseBlockCipher return cipher.getUpdateOutputSize(len); } + public void updateAAD(byte[] input, int offset, int length) + { + throw new UnsupportedOperationException("AAD is not supported in the current mode."); + } + public int processByte(byte in, byte[] out, int outOff) throws DataLengthException { return cipher.processByte(in, out, outOff); @@ -901,6 +1014,11 @@ public class BaseBlockCipher return cipher.getUpdateOutputSize(len); } + public void updateAAD(byte[] input, int offset, int length) + { + cipher.processAADBytes(input, offset, length); + } + public int processByte(byte in, byte[] out, int outOff) throws DataLengthException { return cipher.processByte(in, out, outOff); diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java index 442dcdd..270d648 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java @@ -4,6 +4,9 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.spec.AlgorithmParameterSpec; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; import javax.crypto.MacSpi; import javax.crypto.spec.IvParameterSpec; @@ -13,6 +16,8 @@ import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.crypto.params.SkeinParameters; +import org.bouncycastle.jcajce.spec.SkeinParameterSpec; public class BaseMac extends MacSpi implements PBE @@ -74,6 +79,10 @@ public class BaseMac { param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV()); } + else if (params instanceof SkeinParameterSpec) + { + param = new SkeinParameters.Builder(copyMap(((SkeinParameterSpec)params).getParameters())).setKey(key.getEncoded()).build(); + } else if (params == null) { param = new KeyParameter(key.getEncoded()); @@ -118,4 +127,18 @@ public class BaseMac return out; } + + private static Hashtable copyMap(Map paramsMap) + { + Hashtable newTable = new Hashtable(); + + Iterator keys = paramsMap.keySet().iterator(); + while (keys.hasNext()) + { + Object key = keys.next(); + newTable.put(key, paramsMap.get(key)); + } + + return newTable; + } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java index 6feab0e..31ba38f 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java @@ -3,6 +3,7 @@ package org.bouncycastle.jcajce.provider.symmetric.util; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; +import java.security.InvalidParameterException; import java.security.Key; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; @@ -201,7 +202,7 @@ public class BaseStreamCipher } else { - throw new IllegalArgumentException("unknown parameter type."); + throw new InvalidAlgorithmParameterException("unknown parameter type."); } if ((ivLength != 0) && !(param instanceof ParametersWithIV)) @@ -227,18 +228,25 @@ public class BaseStreamCipher } } - switch (opmode) + try + { + switch (opmode) + { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + throw new InvalidParameterException("unknown opmode " + opmode + " passed"); + } + } + catch (Exception e) { - case Cipher.ENCRYPT_MODE: - case Cipher.WRAP_MODE: - cipher.init(true, param); - break; - case Cipher.DECRYPT_MODE: - case Cipher.UNWRAP_MODE: - cipher.init(false, param); - break; - default: - System.out.println("eeek!"); + throw new InvalidKeyException(e.getMessage()); } } diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java index f16de3c..fac3ead 100644 --- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java @@ -72,7 +72,32 @@ public interface PBE } else if (type == PKCS5S2 || type == PKCS5S2_UTF8) { - generator = new PKCS5S2ParametersGenerator(); + switch (hash) + { + case MD2: + generator = new PKCS5S2ParametersGenerator(new MD2Digest()); + break; + case MD5: + generator = new PKCS5S2ParametersGenerator(new MD5Digest()); + break; + case SHA1: + generator = new PKCS5S2ParametersGenerator(new SHA1Digest()); + break; + case RIPEMD160: + generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest()); + break; + case TIGER: + generator = new PKCS5S2ParametersGenerator(new TigerDigest()); + break; + case SHA256: + generator = new PKCS5S2ParametersGenerator(new SHA256Digest()); + break; + case GOST3411: + generator = new PKCS5S2ParametersGenerator(new GOST3411Digest()); + break; + default: + throw new IllegalStateException("unknown digest scheme for PBE PKCS5S2 encryption."); + } } else if (type == PKCS12) { @@ -261,9 +286,9 @@ public interface PBE key = convertPassword(type, keySpec); generator.init(key, keySpec.getSalt(), keySpec.getIterationCount()); - + param = generator.generateDerivedMacParameters(keySize); - + for (int i = 0; i != key.length; i++) { key[i] = 0; diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java new file mode 100644 index 0000000..be341c4 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java @@ -0,0 +1,108 @@ +package org.bouncycastle.jcajce.spec; + +import java.security.spec.AlgorithmParameterSpec; +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.crypto.engines.GOST28147Engine; +import org.bouncycastle.util.Arrays; + +/** + * A parameter spec for the GOST-28147 cipher. + */ +public class GOST28147ParameterSpec + implements AlgorithmParameterSpec +{ + private byte[] iv = null; + private byte[] sBox = null; + + public GOST28147ParameterSpec( + byte[] sBox) + { + this.sBox = new byte[sBox.length]; + + System.arraycopy(sBox, 0, this.sBox, 0, sBox.length); + } + + public GOST28147ParameterSpec( + byte[] sBox, + byte[] iv) + { + this(sBox); + this.iv = new byte[iv.length]; + + System.arraycopy(iv, 0, this.iv, 0, iv.length); + } + + public GOST28147ParameterSpec( + String sBoxName) + { + this.sBox = GOST28147Engine.getSBox(sBoxName); + } + + public GOST28147ParameterSpec( + String sBoxName, + byte[] iv) + { + this(sBoxName); + this.iv = new byte[iv.length]; + + System.arraycopy(iv, 0, this.iv, 0, iv.length); + } + + public GOST28147ParameterSpec( + ASN1ObjectIdentifier sBoxName, + byte[] iv) + { + this(getName(sBoxName)); + this.iv = Arrays.clone(iv); + } + + public byte[] getSbox() + { + return sBox; + } + + /** + * Returns the IV or null if this parameter set does not contain an IV. + * + * @return the IV or null if this parameter set does not contain an IV. + */ + public byte[] getIV() + { + if (iv == null) + { + return null; + } + + byte[] tmp = new byte[iv.length]; + + System.arraycopy(iv, 0, tmp, 0, tmp.length); + + return tmp; + } + + private static Map oidMappings = new HashMap(); + + static + { + oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet, "E-A"); + oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_B_ParamSet, "E-B"); + oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_C_ParamSet, "E-C"); + oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_D_ParamSet, "E-D"); + } + + private static String getName(ASN1ObjectIdentifier sBoxOid) + { + String sBoxName = (String)oidMappings.get(sBoxOid); + + if (sBoxName == null) + { + throw new IllegalArgumentException("unknown OID: " + sBoxOid); + } + + return sBoxName; + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/PBKDF2KeySpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/PBKDF2KeySpec.java new file mode 100644 index 0000000..214a5eb --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/PBKDF2KeySpec.java @@ -0,0 +1,23 @@ +package org.bouncycastle.jcajce.spec; + +import javax.crypto.spec.PBEKeySpec; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; + +public class PBKDF2KeySpec + extends PBEKeySpec +{ + private AlgorithmIdentifier prf; + + public PBKDF2KeySpec(char[] password, byte[] salt, int iterationCount, int keySize, AlgorithmIdentifier prf) + { + super(password, salt, iterationCount, keySize); + + this.prf = prf; + } + + public AlgorithmIdentifier getPrf() + { + return prf; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/RepeatedSecretKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/RepeatedSecretKeySpec.java new file mode 100644 index 0000000..6af15db --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/RepeatedSecretKeySpec.java @@ -0,0 +1,34 @@ +package org.bouncycastle.jcajce.spec; + + +import javax.crypto.SecretKey; + +/** + * A simple object to indicate that a symmetric cipher should reuse the + * last key provided. + */ +public class RepeatedSecretKeySpec + implements SecretKey +{ + private String algorithm; + + public RepeatedSecretKeySpec(String algorithm) + { + this.algorithm = algorithm; + } + + public String getAlgorithm() + { + return algorithm; + } + + public String getFormat() + { + return null; + } + + public byte[] getEncoded() + { + return null; + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java new file mode 100644 index 0000000..b43aa95 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java @@ -0,0 +1,283 @@ +package org.bouncycastle.jcajce.spec; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.security.spec.AlgorithmParameterSpec; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Integers; + +/** + * Parameters for the Skein hash function - a series of byte[] strings identified by integer tags. + * <p/> + * Parameterised Skein can be used for: + * <ul> + * <li>MAC generation, by providing a {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setKey(byte[]) key}.</li> + * <li>Randomised hashing, by providing a {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setNonce(byte[]) nonce}.</li> + * <li>A hash function for digital signatures, associating a + * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setPublicKey(byte[]) public key} with the message digest.</li> + * <li>A key derivation function, by providing a + * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setKeyIdentifier(byte[]) key identifier}.</li> + * <li>Personalised hashing, by providing a + * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setPersonalisation(java.util.Date, String, String) recommended format} or + * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setPersonalisation(byte[]) arbitrary} personalisation string.</li> + * </ul> + * + * @see org.bouncycastle.crypto.digests.SkeinEngine + * @see org.bouncycastle.crypto.digests.SkeinDigest + * @see org.bouncycastle.crypto.macs.SkeinMac + */ +public class SkeinParameterSpec + implements AlgorithmParameterSpec +{ + /** + * The parameter type for a secret key, supporting MAC or KDF functions: {@value + * #PARAM_TYPE_KEY}. + */ + public static final int PARAM_TYPE_KEY = 0; + + /** + * The parameter type for the Skein configuration block: {@value #PARAM_TYPE_CONFIG}. + */ + public static final int PARAM_TYPE_CONFIG = 4; + + /** + * The parameter type for a personalisation string: {@value #PARAM_TYPE_PERSONALISATION}. + */ + public static final int PARAM_TYPE_PERSONALISATION = 8; + + /** + * The parameter type for a public key: {@value #PARAM_TYPE_PUBLIC_KEY}. + */ + public static final int PARAM_TYPE_PUBLIC_KEY = 12; + + /** + * The parameter type for a key identifier string: {@value #PARAM_TYPE_KEY_IDENTIFIER}. + */ + public static final int PARAM_TYPE_KEY_IDENTIFIER = 16; + + /** + * The parameter type for a nonce: {@value #PARAM_TYPE_NONCE}. + */ + public static final int PARAM_TYPE_NONCE = 20; + + /** + * The parameter type for the message: {@value #PARAM_TYPE_MESSAGE}. + */ + public static final int PARAM_TYPE_MESSAGE = 48; + + /** + * The parameter type for the output transformation: {@value #PARAM_TYPE_OUTPUT}. + */ + public static final int PARAM_TYPE_OUTPUT = 63; + + private Map parameters; + + public SkeinParameterSpec() + { + this(new HashMap()); + } + + private SkeinParameterSpec(Map parameters) + { + this.parameters = Collections.unmodifiableMap(parameters); + } + + /** + * Obtains a map of type (Integer) to value (byte[]) for the parameters tracked in this object. + */ + public Map getParameters() + { + return parameters; + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_KEY key parameter}, or <code>null</code> if not + * set. + */ + public byte[] getKey() + { + return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY))); + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_PERSONALISATION personalisation parameter}, or + * <code>null</code> if not set. + */ + public byte[] getPersonalisation() + { + return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PERSONALISATION))); + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_PUBLIC_KEY public key parameter}, or + * <code>null</code> if not set. + */ + public byte[] getPublicKey() + { + return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PUBLIC_KEY))); + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_KEY_IDENTIFIER key identifier parameter}, or + * <code>null</code> if not set. + */ + public byte[] getKeyIdentifier() + { + return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY_IDENTIFIER))); + } + + /** + * Obtains the value of the {@link #PARAM_TYPE_NONCE nonce parameter}, or <code>null</code> if + * not set. + */ + public byte[] getNonce() + { + return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_NONCE))); + } + + /** + * A builder for {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec}. + */ + public static class Builder + { + private Map parameters = new HashMap(); + + public Builder() + { + } + + public Builder(SkeinParameterSpec params) + { + Iterator keys = params.parameters.keySet().iterator(); + while (keys.hasNext()) + { + Integer key = (Integer)keys.next(); + parameters.put(key, params.parameters.get(key)); + } + } + + /** + * Sets a parameters to apply to the Skein hash function.<br> + * Parameter types must be in the range 0,5..62, and cannot use the value {@value + * org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE} (reserved for message body). + * <p/> + * Parameters with type < {@value org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE} are processed before + * the message content, parameters with type > {@value org.bouncycastle.jcajce.spec.SkeinParameterSpec#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 org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_KEY} parameter. + */ + public Builder setKey(byte[] key) + { + return set(PARAM_TYPE_KEY, key); + } + + /** + * Sets the {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_PERSONALISATION} parameter. + */ + public Builder setPersonalisation(byte[] personalisation) + { + return set(PARAM_TYPE_PERSONALISATION, personalisation); + } + + /** + * Implements the recommended personalisation format for Skein defined in Section 4.11 of + * the Skein 1.3 specification. + * <p/> + * The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte + * sequence using UTF-8 encoding. + * + * @param date the date the personalised application of the Skein was defined. + * @param emailAddress the email address of the creation of the personalised application. + * @param distinguisher an arbitrary personalisation string distinguishing the application. + * @return + */ + public Builder setPersonalisation(Date date, String emailAddress, String distinguisher) + { + try + { + final ByteArrayOutputStream bout = new ByteArrayOutputStream(); + final OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8"); + final DateFormat format = new SimpleDateFormat("YYYYMMDD"); + out.write(format.format(date)); + out.write(" "); + out.write(emailAddress); + out.write(" "); + out.write(distinguisher); + out.close(); + return set(PARAM_TYPE_PERSONALISATION, bout.toByteArray()); + } + catch (IOException e) + { + throw new IllegalStateException("Byte I/O failed: " + e); + } + } + + /** + * Sets the {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_KEY_IDENTIFIER} parameter. + */ + public Builder setPublicKey(byte[] publicKey) + { + return set(PARAM_TYPE_PUBLIC_KEY, publicKey); + } + + /** + * Sets the {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_KEY_IDENTIFIER} parameter. + */ + public Builder setKeyIdentifier(byte[] keyIdentifier) + { + return set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier); + } + + /** + * Sets the {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_NONCE} parameter. + */ + public Builder setNonce(byte[] nonce) + { + return set(PARAM_TYPE_NONCE, nonce); + } + + /** + * Constructs a new {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec} instance with the parameters provided to this + * builder. + */ + public SkeinParameterSpec build() + { + return new SkeinParameterSpec(parameters); + } + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java b/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java index cab5a45..941f476 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java @@ -1,13 +1,8 @@ package org.bouncycastle.jce; import java.util.Enumeration; -import java.util.Vector; import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.nist.NISTNamedCurves; -import org.bouncycastle.asn1.sec.SECNamedCurves; -import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves; -import org.bouncycastle.asn1.x9.X962NamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; @@ -26,12 +21,12 @@ public class ECNamedCurveTable public static ECNamedCurveParameterSpec getParameterSpec( String name) { - X9ECParameters ecP = X962NamedCurves.getByName(name); + X9ECParameters ecP = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName(name); if (ecP == null) { try { - ecP = X962NamedCurves.getByOID(new ASN1ObjectIdentifier(name)); + ecP = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(name)); } catch (IllegalArgumentException e) { @@ -41,43 +36,6 @@ public class ECNamedCurveTable if (ecP == null) { - ecP = SECNamedCurves.getByName(name); - if (ecP == null) - { - try - { - ecP = SECNamedCurves.getByOID(new ASN1ObjectIdentifier(name)); - } - catch (IllegalArgumentException e) - { - // ignore - not an oid - } - } - } - - if (ecP == null) - { - ecP = TeleTrusTNamedCurves.getByName(name); - if (ecP == null) - { - try - { - ecP = TeleTrusTNamedCurves.getByOID(new ASN1ObjectIdentifier(name)); - } - catch (IllegalArgumentException e) - { - // ignore - not an oid - } - } - } - - if (ecP == null) - { - ecP = NISTNamedCurves.getByName(name); - } - - if (ecP == null) - { return null; } @@ -97,23 +55,6 @@ public class ECNamedCurveTable */ 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()); - } + return org.bouncycastle.asn1.x9.ECNamedCurveTable.getNames(); } } diff --git a/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java b/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java index 3518583..5ff966a 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java @@ -50,7 +50,7 @@ public class ECPointUtil } org.bouncycastle.math.ec.ECPoint p = c.decodePoint(encoded); - - return new ECPoint(p.getX().toBigInteger(), p.getY().toBigInteger()); + + return new ECPoint(p.getAffineXCoord().toBigInteger(), p.getAffineYCoord().toBigInteger()); } } diff --git a/bcprov/src/main/java/org/bouncycastle/jce/X509Principal.java b/bcprov/src/main/java/org/bouncycastle/jce/X509Principal.java index efa0f66..ddd38e8 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/X509Principal.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/X509Principal.java @@ -19,6 +19,7 @@ import org.bouncycastle.asn1.x509.X509Name; * PrincipalUtil class. * </p> * @see org.bouncycastle.jce.PrincipalUtil + * @deprecated use the X500Name class. */ public class X509Principal extends X509Name diff --git a/bcprov/src/main/java/org/bouncycastle/jce/examples/package.html b/bcprov/src/main/java/org/bouncycastle/jce/examples/package.html deleted file mode 100644 index 96b3193..0000000 --- a/bcprov/src/main/java/org/bouncycastle/jce/examples/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Example classes for use with the JCE. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/jce/interfaces/package.html b/bcprov/src/main/java/org/bouncycastle/jce/interfaces/package.html deleted file mode 100644 index bacde6c..0000000 --- a/bcprov/src/main/java/org/bouncycastle/jce/interfaces/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Interfaces for supporting Elliptic Curve Keys, El Gamal, and PKCS12 attributes. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/jce/package.html b/bcprov/src/main/java/org/bouncycastle/jce/package.html deleted file mode 100644 index 52ef3bf..0000000 --- a/bcprov/src/main/java/org/bouncycastle/jce/package.html +++ /dev/null @@ -1,10 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Utility classes for use with the JCE. -<p> -The classes in this package support the generation of certificates and PKCS10 signing requests. -<p> -Note: the PKCS7 class is deprecated, for a fuller version of CMS see the cms package distributed -with the BC mail API. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index 0433965..dc7db18 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -44,7 +44,7 @@ import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; public final class BouncyCastleProvider extends Provider implements ConfigurableProvider { - private static String info = "BouncyCastle Security Provider v1.49"; + private static String info = "BouncyCastle Security Provider v1.50"; public static final String PROVIDER_NAME = "BC"; @@ -69,8 +69,10 @@ public final class BouncyCastleProvider extends Provider private static final String[] SYMMETRIC_CIPHERS = { - "AES", "ARC4", "Blowfish", "Camellia", "CAST5", "CAST6", "DES", "DESede", "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA", - "Noekeon", "RC2", "RC5", "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Skipjack", "TEA", "Twofish", "VMPC", "VMPCKSA3", "XTEA" + "AES", "ARC4", "Blowfish", "Camellia", "CAST5", "CAST6", "ChaCha", "DES", "DESede", + "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA", "Noekeon", "RC2", "RC5", + "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Shacal2", "Skipjack", "TEA", "Twofish", "Threefish", + "VMPC", "VMPCKSA3", "XTEA", "XSalsa20" }; /* @@ -96,11 +98,11 @@ public final class BouncyCastleProvider extends Provider private static final String DIGEST_PACKAGE = "org.bouncycastle.jcajce.provider.digest."; private static final String[] DIGESTS = { - "GOST3411", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "Tiger", "Whirlpool" + "GOST3411", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "Skein", "SM3", "Tiger", "Whirlpool" }; /* - * Configurable digests + * Configurable keystores */ private static final String KEYSTORE_PACKAGE = "org.bouncycastle.jcajce.provider.keystore."; private static final String[] KEYSTORES = @@ -115,7 +117,7 @@ public final class BouncyCastleProvider extends Provider */ public BouncyCastleProvider() { - super(PROVIDER_NAME, 1.49, info); + super(PROVIDER_NAME, 1.50, info); AccessController.doPrivileged(new PrivilegedAction() { diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java index 3175237..63bb6d8 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java @@ -125,8 +125,8 @@ public class JCEECPrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), dp.getN(), dp.getH().intValue()); } @@ -156,8 +156,8 @@ public class JCEECPrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), dp.getN(), dp.getH().intValue()); } @@ -168,8 +168,8 @@ public class JCEECPrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), spec.getN(), spec.getH().intValue()); } @@ -212,8 +212,8 @@ public class JCEECPrivateKey ECGOST3410NamedCurves.getName(oid), ellipticCurve, new ECPoint( - gParam.getG().getX().toBigInteger(), - gParam.getG().getY().toBigInteger()), + gParam.getG().getAffineXCoord().toBigInteger(), + gParam.getG().getAffineYCoord().toBigInteger()), gParam.getN(), gParam.getH()); } @@ -225,8 +225,8 @@ public class JCEECPrivateKey ECUtil.getCurveName(oid), ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH()); } @@ -243,8 +243,8 @@ public class JCEECPrivateKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH().intValue()); } diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java index 00df81f..4bf2e68 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java @@ -88,7 +88,7 @@ public class JCEECPublicKey { org.bouncycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa(); - q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false); + q = s.getCurve().createPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger(), false); } this.ecSpec = null; } @@ -157,8 +157,8 @@ public class JCEECPublicKey return new ECParameterSpec( ellipticCurve, new ECPoint( - dp.getG().getX().toBigInteger(), - dp.getG().getY().toBigInteger()), + dp.getG().getAffineXCoord().toBigInteger(), + dp.getG().getAffineYCoord().toBigInteger()), dp.getN(), dp.getH().intValue()); } @@ -221,8 +221,8 @@ public class JCEECPublicKey ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()), ellipticCurve, new ECPoint( - spec.getG().getX().toBigInteger(), - spec.getG().getY().toBigInteger()), + spec.getG().getAffineXCoord().toBigInteger(), + spec.getG().getAffineYCoord().toBigInteger()), spec.getN(), spec.getH()); } @@ -244,8 +244,8 @@ public class JCEECPublicKey ECUtil.getCurveName(oid), ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH()); } @@ -264,8 +264,8 @@ public class JCEECPublicKey this.ecSpec = new ECParameterSpec( ellipticCurve, new ECPoint( - ecP.getG().getX().toBigInteger(), - ecP.getG().getY().toBigInteger()), + ecP.getG().getAffineXCoord().toBigInteger(), + ecP.getG().getAffineYCoord().toBigInteger()), ecP.getN(), ecP.getH().intValue()); } @@ -344,8 +344,8 @@ public class JCEECPublicKey } } - BigInteger bX = this.q.getX().toBigInteger(); - BigInteger bY = this.q.getY().toBigInteger(); + BigInteger bX = this.q.getAffineXCoord().toBigInteger(); + BigInteger bY = this.q.getAffineYCoord().toBigInteger(); byte[] encKey = new byte[64]; extractBytes(encKey, 0, bX); @@ -391,7 +391,7 @@ public class JCEECPublicKey ECCurve curve = this.engineGetQ().getCurve(); ASN1OctetString p = (ASN1OctetString) - new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive(); + new X9ECPoint(curve.createPoint(this.getQ().getAffineXCoord().toBigInteger(), this.getQ().getAffineYCoord().toBigInteger(), withCompression)).toASN1Primitive(); info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets()); } @@ -432,7 +432,7 @@ public class JCEECPublicKey public ECPoint getW() { - return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger()); + return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger()); } public org.bouncycastle.math.ec.ECPoint getQ() @@ -441,11 +441,11 @@ public class JCEECPublicKey { if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp) { - return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY()); + return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord()); } else { - return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY()); + return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord()); } } @@ -473,8 +473,8 @@ public class JCEECPublicKey String nl = System.getProperty("line.separator"); buf.append("EC Public Key").append(nl); - buf.append(" X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl); - buf.append(" Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl); + buf.append(" X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl); + buf.append(" Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl); return buf.toString(); diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java index d5c3700..7e76a89 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java @@ -211,6 +211,23 @@ public class X509CRLEntryObject extends X509CRLEntry return hashValue; } + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o instanceof X509CRLEntryObject) + { + X509CRLEntryObject other = (X509CRLEntryObject)o; + + return this.c.equals(other.c); + } + + return super.equals(this); + } + public byte[] getEncoded() throws CRLException { diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java index cd83211..b5b4f13 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java @@ -59,6 +59,8 @@ public class X509CRLObject private String sigAlgName; private byte[] sigAlgParams; private boolean isIndirect; + private boolean isHashCodeSet = false; + private int hashCodeValue; static boolean isIndirectCRL(X509CRL crl) throws CRLException @@ -520,7 +522,7 @@ public class X509CRLObject throw new RuntimeException("X.509 CRL used with non X.509 Cert"); } - TBSCertList.CRLEntry[] certs = c.getRevokedCertificates(); + Enumeration certs = c.getRevokedCertificateEnumeration(); X500Name caName = c.getIssuer(); @@ -528,11 +530,13 @@ public class X509CRLObject { BigInteger serial = ((X509Certificate)cert).getSerialNumber(); - for (int i = 0; i < certs.length; i++) + while (certs.hasMoreElements()) { - if (isIndirect && certs[i].hasExtensions()) + TBSCertList.CRLEntry entry = TBSCertList.CRLEntry.getInstance(certs.nextElement()); + + if (isIndirect && entry.hasExtensions()) { - Extension currentCaName = certs[i].getExtensions().getExtension(Extension.certificateIssuer); + Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer); if (currentCaName != null) { @@ -540,7 +544,7 @@ public class X509CRLObject } } - if (certs[i].getUserCertificate().getValue().equals(serial)) + if (entry.getUserCertificate().getValue().equals(serial)) { X500Name issuer; @@ -572,5 +576,50 @@ public class X509CRLObject return false; } + + public boolean equals(Object other) + { + if (this == other) + { + return true; + } + + if (!(other instanceof X509CRL)) + { + return false; + } + + if (other instanceof X509CRLObject) + { + X509CRLObject crlObject = (X509CRLObject)other; + + if (isHashCodeSet) + { + boolean otherIsHashCodeSet = crlObject.isHashCodeSet; + if (otherIsHashCodeSet) + { + if (crlObject.hashCodeValue != hashCodeValue) + { + return false; + } + } + } + + return this.c.equals(crlObject.c); + } + + return super.equals(other); + } + + public int hashCode() + { + if (!isHashCodeSet) + { + isHashCodeSet = true; + hashCodeValue = super.hashCode(); + } + + return hashCodeValue; + } } diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AEADTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AEADTest.java new file mode 100644 index 0000000..d2f1405 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AEADTest.java @@ -0,0 +1,241 @@ +package org.bouncycastle.jce.provider.test; + +import java.io.IOException; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Security; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.asn1.cms.GCMParameters; +import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +public class AEADTest extends SimpleTest +{ + + // EAX test vector from EAXTest + 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"); + // C2 with only 64bit MAC (default for EAX) + private byte[] C2_short = Hex.decode("19DD5C4C9331049D0BDA"); + + private byte[] KGCM = Hex.decode("00000000000000000000000000000000"); + private byte[] NGCM = Hex.decode("000000000000000000000000"); + private byte[] CGCM = Hex.decode("58e2fccefa7e3061367f1d57a4e7455a"); + + public String getName() + { + return "AEAD"; + } + + public void performTest() throws Exception + { + try + { + this.getClass().getClassLoader().loadClass("javax.crypto.spec.GCMParameterSpec"); + + checkCipherWithAD(K2, N2, A2, P2, C2_short); + testGCMParameterSpec(K2, N2, A2, P2, C2); + testGCMParameterSpecWithRepeatKey(K2, N2, A2, P2, C2); + testGCMGeneric(KGCM, NGCM, new byte[0], new byte[0], CGCM); + } + catch (ClassNotFoundException e) + { + System.err.println("AEADTest disabled due to JDK"); + } + } + + private void checkCipherWithAD(byte[] K, + byte[] N, + byte[] A, + byte[] P, + byte[] C) throws InvalidKeyException, + NoSuchAlgorithmException, NoSuchPaddingException, + IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException, NoSuchProviderException + { + Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC"); + SecretKeySpec key = new SecretKeySpec(K, "AES"); + IvParameterSpec iv = new IvParameterSpec(N); + eax.init(Cipher.ENCRYPT_MODE, key, iv); + + eax.updateAAD(A); + byte[] c = eax.doFinal(P); + + if (!areEqual(C, c)) + { + fail("JCE encrypt with additional data failed."); + } + + eax.init(Cipher.DECRYPT_MODE, key, iv); + eax.updateAAD(A); + byte[] p = eax.doFinal(C); + + if (!areEqual(P, p)) + { + fail("JCE decrypt with additional data failed."); + } + } + + private void testGCMParameterSpec(byte[] K, + byte[] N, + byte[] A, + byte[] P, + byte[] C) + throws InvalidKeyException, + NoSuchAlgorithmException, NoSuchPaddingException, + IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException, NoSuchProviderException, IOException + { + Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC"); + SecretKeySpec key = new SecretKeySpec(K, "AES"); + + // GCMParameterSpec mapped to AEADParameters and overrides default MAC + // size + GCMParameterSpec spec = new GCMParameterSpec(128, N); + eax.init(Cipher.ENCRYPT_MODE, key, spec); + + eax.updateAAD(A); + byte[] c = eax.doFinal(P); + + if (!areEqual(C, c)) + { + fail("JCE encrypt with additional data and GCMParameterSpec failed."); + } + + eax.init(Cipher.DECRYPT_MODE, key, spec); + eax.updateAAD(A); + byte[] p = eax.doFinal(C); + + if (!areEqual(P, p)) + { + fail("JCE decrypt with additional data and GCMParameterSpec failed."); + } + + AlgorithmParameters algParams = eax.getParameters(); + + byte[] encParams = algParams.getEncoded(); + + GCMParameters gcmParameters = GCMParameters.getInstance(encParams); + + if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce()) || spec.getTLen() != gcmParameters.getIcvLen()) + { + fail("parameters mismatch"); + } + } + + private void testGCMParameterSpecWithRepeatKey(byte[] K, + byte[] N, + byte[] A, + byte[] P, + byte[] C) + throws InvalidKeyException, NoSuchAlgorithmException, + NoSuchPaddingException, IllegalBlockSizeException, + BadPaddingException, InvalidAlgorithmParameterException, NoSuchProviderException, IOException + { + Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC"); + SecretKeySpec key = new SecretKeySpec(K, "AES"); + GCMParameterSpec spec = new GCMParameterSpec(128, N); + eax.init(Cipher.ENCRYPT_MODE, key, spec); + + eax.updateAAD(A); + byte[] c = eax.doFinal(P); + + if (!areEqual(C, c)) + { + fail("JCE encrypt with additional data and RepeatedSecretKeySpec failed."); + } + + // Check GCMParameterSpec handling knows about RepeatedSecretKeySpec + eax.init(Cipher.DECRYPT_MODE, new RepeatedSecretKeySpec("AES"), spec); + eax.updateAAD(A); + byte[] p = eax.doFinal(C); + + if (!areEqual(P, p)) + { + fail("JCE decrypt with additional data and RepeatedSecretKeySpec failed."); + } + + AlgorithmParameters algParams = eax.getParameters(); + + byte[] encParams = algParams.getEncoded(); + + GCMParameters gcmParameters = GCMParameters.getInstance(encParams); + + if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce()) || spec.getTLen() != gcmParameters.getIcvLen()) + { + fail("parameters mismatch"); + } + } + + private void testGCMGeneric(byte[] K, + byte[] N, + byte[] A, + byte[] P, + byte[] C) + throws InvalidKeyException, + NoSuchAlgorithmException, NoSuchPaddingException, + IllegalBlockSizeException, BadPaddingException, + InvalidAlgorithmParameterException, NoSuchProviderException, IOException + { + Cipher eax = Cipher.getInstance("AES/GCM/NoPadding", "BC"); + SecretKeySpec key = new SecretKeySpec(K, "AES"); + + // GCMParameterSpec mapped to AEADParameters and overrides default MAC + // size + GCMParameterSpec spec = new GCMParameterSpec(128, N); + eax.init(Cipher.ENCRYPT_MODE, key, spec); + + eax.updateAAD(A); + byte[] c = eax.doFinal(P); + + if (!areEqual(C, c)) + { + fail("JCE encrypt with additional data and GCMParameterSpec failed."); + } + + eax = Cipher.getInstance("GCM", "BC"); + eax.init(Cipher.DECRYPT_MODE, key, spec); + eax.updateAAD(A); + byte[] p = eax.doFinal(C); + + if (!areEqual(P, p)) + { + fail("JCE decrypt with additional data and GCMParameterSpec failed."); + } + + AlgorithmParameters algParams = eax.getParameters(); + + byte[] encParams = algParams.getEncoded(); + + GCMParameters gcmParameters = GCMParameters.getInstance(encParams); + + if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce()) || spec.getTLen() != gcmParameters.getIcvLen()) + { + fail("parameters mismatch"); + } + } + + public static void main(String[] args) throws Exception + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new AEADTest()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AESSICTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AESSICTest.java index 61f7995..ae6d7bc 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AESSICTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AESSICTest.java @@ -7,8 +7,8 @@ import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.RepeatedSecretKeySpec; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java index 30489bf..0c9cf01 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java @@ -1,22 +1,5 @@ package org.bouncycastle.jce.provider.test; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.util.encoders.Hex; -import org.bouncycastle.util.test.SimpleTest; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.KeyGenerator; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.ShortBufferException; -import javax.crypto.spec.DESedeKeySpec; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.RC2ParameterSpec; -import javax.crypto.spec.RC5ParameterSpec; -import javax.crypto.spec.SecretKeySpec; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -33,6 +16,24 @@ import java.security.Security; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + /** * basic test class for a block cipher, basically this just exercises the provider, and makes sure we * are behaving sensibly, correctness of the implementation is shown in the lightweight test classes. @@ -204,6 +205,12 @@ public class BlockCipherTest "6cd6f7c5d2c655556d7a9e98a1696d1875e9f1b2fc991e28a2d55b56861e80bd", "Twofish/OFB/NoPadding", "821c54b1b54ae113cf74595eefe10c83b61c9682fc81f92c52f39a3a693f88b8", + "Threefish-256/OFB/NoPadding", + "546ea995dd302f1efcb1f27d14bad468280a3a7994c2af75dfdf1e9fc5ef2373", + "Threefish-512/OFB/NoPadding", + "152df966484ecc2e9ddfc386559732f7f632e4008920804a1bde4efcf2e6e2f2", + "Threefish-1024/OFB/NoPadding", + "03953ac751a7377812c6e3e4d14b36c6953f9b390acaa892811c10001c9be454", "RC2/OFB/NoPadding", "0a07cb78537cb04c0c74e28a7b86b80f80acadf87d6ef32792f1a8cf74b39f74", "RC5/OFB/NoPadding", @@ -220,6 +227,12 @@ public class BlockCipherTest "6ca6078755b263f09787d830b6fda7b7748494634bdc73ab68540cf9f6b7eccf", "Twofish/OFB8/NoPadding", "825dcec234ad52253d6e064b0d769bc04b1142435933f4a510ffc20d70095a88", + "Threefish-256/OFB8/NoPadding", + "545fbd92313512127218262dd4394569aca96ba122e1432b661ecfc01af3a25c", + "Threefish-512/OFB8/NoPadding", + "15f6e7d215662c525ea982cab56409cf833157e1af06edd57a13c71487904fea", + "Threefish-1024/OFB8/NoPadding", + "03d80b67ff7139d9dd8b07280642f94074496e5fc37b1ba1f8593cdf64a1e4ca", "RC2/OFB8/NoPadding", "0aa26c6f6a820fe7d38da97085995ad62e2e293323a76300fcd4eb572810f7c6", "RC5/OFB8/NoPadding", @@ -236,6 +249,12 @@ public class BlockCipherTest "6cd6f7c5d2c6555561167fe9b10665102206869339122f1ed89efa4a985397f6", "Twofish/CFB/NoPadding", "821c54b1b54ae113cf74595eefe10c8308b7a438277de4f40948ac2d172d53d2", + "Threefish-256/CFB/NoPadding", + "546ea995dd302f1efcb1f27d14bad468280a3a7994c2af75dfdf1e9fc5ef2373", + "Threefish-512/CFB/NoPadding", + "152df966484ecc2e9ddfc386559732f7f632e4008920804a1bde4efcf2e6e2f2", + "Threefish-1024/CFB/NoPadding", + "03953ac751a7377812c6e3e4d14b36c6953f9b390acaa892811c10001c9be454", "RC2/CFB/NoPadding", "0a07cb78537cb04ca1401450d5cd411c7da7fa5b6baaa17bb2137bd95c9f26a5", "RC5/CFB/NoPadding", @@ -252,6 +271,12 @@ public class BlockCipherTest "6ca63aaada9188d2410c07513cc0736b9888770768c25a5befc776beea5bdc4c", "Twofish/CFB8/NoPadding", "825d12af040721cf5ed4a4798647837ac5eb14d752aace28728aeb37b2010abd", + "Threefish-256/CFB8/NoPadding", + "545fbf0a4b925f399cf7540f1cc1cc6012e329ab2d4db0aa0dfa29ee2a2019d1", + "Threefish-512/CFB8/NoPadding", + "15f695964f20b95ed72afad75f905788839c53bed2ae5fdfdfb13e3241fd7f94", + "Threefish-1024/CFB8/NoPadding", + "03d897c89e740d2254f717b73315151d9a34c829e4162232b3cd5f5158ff367b", "RC2/CFB8/NoPadding", "0aa227f94be3a32ff927c5d25647ea41d7c2a1e94012fc7f2ad6767b9664bce5", "RC5/CFB8/NoPadding", @@ -265,7 +290,29 @@ public class BlockCipherTest "Twofish/ECB/TBCPadding", "70336d9c9718a8a2ced1b19deed973a3c58af7ea71a69e7efc4df082dca581c019d7daa58d02b89aab6e8c0d17202439", "RC2/ECB/TBCPadding", - "eb5b889bbcced12eb6b1a3da6a3d965bba66a5edfdd4c8a6b6b1a3da6a3d965b6b5359ba5e69b179" + "eb5b889bbcced12eb6b1a3da6a3d965bba66a5edfdd4c8a6b6b1a3da6a3d965b6b5359ba5e69b179", + "DES/CTR/NoPadding", + "537572e480c1714fb47081d35eb18eaca9e0a5aee982f105438a0db6cece1f6d", + "DESede/CTR/NoPadding", + "481e9872acea7fcfa93b7d4e34ec7bab340c10faba2e43b879d40d38e07c422d", + "SKIPJACK/CTR/NoPadding", + "71143a124e3a0cdeee98a7b843baa05bd1d59faee8ec9b89880e070314a04cc2", + "Blowfish/CTR/NoPadding", + "6cd6f7c5d2c65555d2b31f8614f54ec654f5e7888d515008d59302c3edfcc6cb", + "Twofish/CTR/NoPadding", + "821c54b1b54ae113cf74595eefe10c83d09e95d4599190b9bbd5bc71dd703730", + "Threefish-256/CTR/NoPadding", + "546ea995dd302f1efcb1f27d14bad468280a3a7994c2af75dfdf1e9fc5ef2373", + "Threefish-512/CTR/NoPadding", + "152df966484ecc2e9ddfc386559732f7f632e4008920804a1bde4efcf2e6e2f2", + "Threefish-1024/CTR/NoPadding", + "03953ac751a7377812c6e3e4d14b36c6953f9b390acaa892811c10001c9be454", + "RC2/CTR/NoPadding", + "0a07cb78537cb04c8c5a0a39a15977a7eb19f3c48a42759c234868c391a99c63", + "RC5/CTR/NoPadding", + "c62b233df296283b97f17364d5f69a1ff91f46659cf9856caefd322a936203a7", + "IDEA/CTR/NoPadding", + "dd447da3cbdcf81f4694ab7715d79e3f90af5682e8c318b8f7dadbed6b5c9714", }; static String[] cipherTests2 = @@ -280,8 +327,127 @@ public class BlockCipherTest "60fa2f8fae5aa2a38e9ac77d0246726b32df660db51a710ceb7511e451" }; + static String[] cipherTestsLargeBlock = + { + "Threefish-256", + "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" + + "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" + + "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" + + "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" + + "31533aa864e6a40edc3e24b36260d94374893dc2e479793292e29c18a6ee01a9", + "Threefish-512", + "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" + + "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" + + "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" + + "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" + + "ad7ec86b2137af1ddb64794d714c4e1d7b687b19fc9781ef887a0ad7f88e18fc" + + "1baa6123ec8bc497e7eb7b5090cfd756fd5333425ed5a240cb96735dea9713d9", + "Threefish-1024", + "df6d789e301c6a5e22e0cff0b44666630d44ce774a41b628ebaff6adc86d9e66" + + "af50a282a4313552bc9b861cb286ab569e2e23b1c97cdb5cb1fde1bacfba9bfb" + + "de3b443218e16b6038537b3d803ff5dbd26b13c177a5bfb597ffccca142a5905" + + "8c0f74623daa96bff95b716674701034e7947ce0541426fa5177bc1a519b23ba" + + "462f1724989612e49ca5e92a0129ec7be576846fe2616664674e16a29ce8679c" + + "0adda9034fbd652910c2ae5afacde10281ab18dbeeb83464dc21ff66b0d358ff" + + "2328c73aca59e9095a7bca94acc79d10038eab6ef865545bcf73f4caeeba1844" + + "6add98350c8276e5abfb8709bb6c01ef3297b862818a4996b744f375b9126e5c", + "Threefish-256/CBC/NoPadding", + "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" + + "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" + + "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3" + + "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e", + "Threefish-512/CBC/NoPadding", + "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" + + "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6" + + "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" + + "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7", + "Threefish-1024/CBC/NoPadding", + "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87" + + "b59be63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb" + + "491f280f4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a" + + "419544ef5907f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003", + "Threefish-256/CBC/PKCS7Padding", + "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" + + "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" + + "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3" + + "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e" + + "f96cb468a5cd39a003f976464a7d072c94cb72a3fe739f101aa7b5452bc3fbba", + "Threefish-512/CBC/PKCS7Padding", + "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" + + "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6" + + "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" + + "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7" + + "03902162280012e59efa15c6beecfbf440a6a0c4474bbbb2f74a0ad31bcd398f" + + "b24728c3605a4ced3c92c30a5e231113abafaf6f83a3867978e3cdd74091d09f", + "Threefish-1024/CBC/PKCS7Padding", + "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87" + + "b59be63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb" + + "491f280f4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a" + + "419544ef5907f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003" + + "4ddd16ad731ad9a32d0f196a72284f7a8df98918e3e22f1708662edeb1810d2b" + + "bafd4200e849f3288b55634b37f99f0f7b2dd192a5944fc211ef9e37b67a829b" + + "005a5ec609f736875fdf8946bd79c1daa6c44c9d6733a2223cf8b7e5203b1cfd" + + "76995f67e570d9c403b2a2e3f3a89c63c7850ee8d47d4398ac377345a139dda4", + "Threefish-256/CTS/NoPadding", + "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" + + "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" + + "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e" + + "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3", + "Threefish-512/CTS/NoPadding", + "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" + + "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7" + + "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" + + "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6", + "Threefish-1024/CTS/NoPadding", + "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87b59b" + + "e63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb491f280f" + + "4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a419544ef5907" + + "f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003", + "Threefish-256/CBC/WithCTS", + "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" + + "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" + + "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e" + + "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3", + "Threefish-512/CBC/WithCTS", + "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" + + "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7" + + "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" + + "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6", + "Threefish-1024/CBC/WithCTS", + "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87b59b" + + "e63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb491f280f" + + "4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a419544ef5907" + + "f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003", + "Threefish-256/ECB/TBCPadding", + "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" + + "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" + + "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" + + "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" + + "89c4e79b90153a821bdd4efd5eb1e2cda89b6a91540a003eef03868472d8cfce", + "Threefish-512/ECB/TBCPadding", + "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" + + "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" + + "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" + + "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" + + "dd6bfa1006e4df51298e382ca397a2c398cdb4d65009dce77c5f0a31f9807218" + + "a72372a8a0df3b1bacd5dbfb116ebbe314e0b0cd64fd2c8ae8a81491c2534a2a", + "Threefish-1024/ECB/TBCPadding", + "df6d789e301c6a5e22e0cff0b44666630d44ce774a41b628ebaff6adc86d9e66" + + "af50a282a4313552bc9b861cb286ab569e2e23b1c97cdb5cb1fde1bacfba9bfb" + + "de3b443218e16b6038537b3d803ff5dbd26b13c177a5bfb597ffccca142a5905" + + "8c0f74623daa96bff95b716674701034e7947ce0541426fa5177bc1a519b23ba" + + "7312262dc3a25984847d1b05cb624f5751946f136ee7bd0a9a4bbac5dd3bd213" + + "702390d3a53d1a4132f59383cce4fe61e08cd3c73c570190d1c8b60940031ef7" + + "42f6775b00fb0b4273a14b46a3fc0e760e02f75dc6100ca9c038c3f151e03145" + + "92686fd8cccbee74d246a8c59ad80205c9f9aaeb100ea5812837ee8699753301", + }; + static byte[] input1 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); static byte[] input2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c"); + static byte[] inputLargeBlock = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f" + + "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f" + + "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f" + + "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); static RC2ParameterSpec rc2Spec = new RC2ParameterSpec(128, Hex.decode("0123456789abcdef")); static RC5ParameterSpec rc5Spec = new RC5ParameterSpec(16, 16, 32, Hex.decode("0123456789abcdef")); @@ -930,6 +1096,11 @@ public class BlockCipherTest test(cipherTests2[i], input2, Hex.decode(cipherTests2[i + 1])); } + for (int i = 0; i != cipherTestsLargeBlock.length; i += 2) + { + test(cipherTestsLargeBlock[i], inputLargeBlock, Hex.decode(cipherTestsLargeBlock[i + 1])); + } + // // check for less than a block // diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java index 83063ef..0110d22 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java @@ -1,8 +1,12 @@ package org.bouncycastle.jce.provider.test; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.util.encoders.Hex; -import org.bouncycastle.util.test.SimpleTest; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; @@ -12,13 +16,10 @@ import javax.crypto.SecretKey; import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.Security; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; /** * check that cipher input/output streams are working correctly @@ -44,6 +45,23 @@ public class CipherStreamTest + "2B4F97E0FF16924A52DF269515110A07" + "F9E460BC65EF95DA58F740B7D1DBB0AA"); + private static final byte[] XSK = Hex.decode("d5c7f6797b7e7e9c1d7fd2610b2abf2bc5a7885fb3ff78092fb3abe8986d35e2"); + private static final byte[] XSIV = Hex.decode("744e17312b27969d826444640e9c4a378ae334f185369c95"); + private static final byte[] XSIN = Hex.decode("7758298c628eb3a4b6963c5445ef66971222be5d1a4ad839715d1188071739b77cc6e05d5410f963a64167629757"); + private static final byte[] XSOUT= Hex.decode("27b8cfe81416a76301fd1eec6a4d99675069b2da2776c360db1bdfea7c0aa613913e10f7a60fec04d11e65f2d64e"); + + private static final byte[] CHAK = Hex.decode("80000000000000000000000000000000"); + private static final byte[] CHAIV = Hex.decode("0000000000000000"); + private static final byte[] CHAIN = Hex.decode( + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000" + + "00000000000000000000000000000000"); + private static final byte[] CHAOUT = Hex.decode("FBB87FBB8395E05DAA3B1D683C422046" + + "F913985C2AD9B23CFC06C1D8D04FF213" + + "D44A7A7CDB84929F915420A8A3DC58BF" + + "0F7ECB4B1F167BB1A5E6153FDAF4493D"); + private static final byte[] HCIN = new byte[64]; private static final byte[] HCIV = new byte[32]; @@ -151,7 +169,7 @@ public class CipherStreamTest byte[] enc = in.doFinal(plainText); if (!areEqual(enc, cipherText)) { - fail(name + ": cipher text doesn't match"); + fail(name + ": cipher text doesn't match got " + new String(Hex.encode(enc))); } byte[] dec = out.doFinal(enc); @@ -184,7 +202,7 @@ public class CipherStreamTest (byte)137, (byte)138, (byte)140, (byte)143 }; byte[] keyBytes; - if (name.equals("HC256")) + if (name.equals("HC256") || name.equals("XSalsa20")) { keyBytes = key256; } @@ -207,18 +225,18 @@ public class CipherStreamTest // too // small to hold the result ecipher.update(new byte[20], 0, 20, cipherText); - + fail("failed exception test - no ShortBufferException thrown"); } catch (ShortBufferException e) { // ignore } - + try { Cipher c = Cipher.getInstance(name, "BC"); - + Key k = new PublicKey() { @@ -236,22 +254,22 @@ public class CipherStreamTest { return null; } - + }; - + c.init(Cipher.ENCRYPT_MODE, k); - + fail("failed exception test - no InvalidKeyException thrown for public key"); } catch (InvalidKeyException e) { // okay } - + try { Cipher c = Cipher.getInstance(name, "BC"); - + Key k = new PrivateKey() { @@ -269,11 +287,11 @@ public class CipherStreamTest { return null; } - + }; - + c.init(Cipher.DECRYPT_MODE, k); - + fail("failed exception test - no InvalidKeyException thrown for private key"); } catch (InvalidKeyException e) @@ -286,7 +304,7 @@ public class CipherStreamTest fail("unexpected exception.", e); } } - + public void performTest() throws Exception { @@ -296,6 +314,12 @@ public class CipherStreamTest runTest("Salsa20"); testException("Salsa20"); testAlgorithm("Salsa20", SK, SIV, SIN, SOUT); + runTest("XSalsa20"); + testException("XSalsa20"); + testAlgorithm("XSalsa20", XSK, XSIV, XSIN, XSOUT); + runTest("ChaCha"); + testException("ChaCha"); + testAlgorithm("ChaCha", CHAK, CHAIV, CHAIN, CHAOUT); runTest("HC128"); testException("HC128"); testAlgorithm("HC128", HCK128A, HCIV, HCIN, HC128A); diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest2.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest2.java new file mode 100644 index 0000000..de9533c --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest2.java @@ -0,0 +1,495 @@ +package org.bouncycastle.jce.provider.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.Key; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.IvParameterSpec; + +import org.bouncycastle.crypto.io.InvalidCipherTextIOException; +import org.bouncycastle.jcajce.io.CipherInputStream; +import org.bouncycastle.jcajce.io.CipherOutputStream; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.test.SimpleTest; + +public class CipherStreamTest2 + extends SimpleTest +{ + public String getName() + { + return "CipherStreamTest"; + } + + private void testModes(String algo, String[] transforms, boolean authenticated) + throws Exception + { + Key key = generateKey(algo); + for (int i = 0; i != transforms.length; i++) + { + String transform = transforms[i]; + + testWriteRead(algo + transform, key, authenticated, true, false); + testWriteRead(algo + transform, key, authenticated, true, true); + testWriteRead(algo + transform, key, authenticated, false, false); + testWriteRead(algo + transform, key, authenticated, false, true); + testReadWrite(algo + transform, key, authenticated, true, false); + testReadWrite(algo + transform, key, authenticated, true, true); + testReadWrite(algo + transform, key, authenticated, false, false); + testReadWrite(algo + transform, key, authenticated, false, true); + + if (!(transform.indexOf("CTS") > -1)) + { + testWriteReadEmpty(algo + transform, key, authenticated, true, false); + testWriteReadEmpty(algo + transform, key, authenticated, true, true); + testWriteReadEmpty(algo + transform, key, authenticated, false, false); + testWriteReadEmpty(algo + transform, key, authenticated, false, true); + } + + if (authenticated) + { + testTamperedRead(algo + transform, key, true, true); + testTamperedRead(algo + transform, key, true, false); + testTruncatedRead(algo + transform, key, true, true); + testTruncatedRead(algo + transform, key, true, false); + testTamperedWrite(algo + transform, key, true, true); + testTamperedWrite(algo + transform, key, true, false); + } + } + } + + private InputStream createInputStream(byte[] data, Cipher cipher, boolean useBc) + { + ByteArrayInputStream bytes = new ByteArrayInputStream(data); + // cast required for earlier JDK + return useBc ? (InputStream)new CipherInputStream(bytes, cipher) : (InputStream)new javax.crypto.CipherInputStream(bytes, cipher); + } + + private OutputStream createOutputStream(ByteArrayOutputStream bytes, Cipher cipher, boolean useBc) + { + // cast required for earlier JDK + return useBc ? (OutputStream)new CipherOutputStream(bytes, cipher) : (OutputStream)new javax.crypto.CipherOutputStream(bytes, cipher); + } + + /** + * Test tampering of ciphertext followed by read from decrypting CipherInputStream + */ + private void testTamperedRead(String name, Key key, boolean authenticated, boolean useBc) + throws Exception + { + Cipher encrypt = Cipher.getInstance(name, "BC"); + Cipher decrypt = Cipher.getInstance(name, "BC"); + encrypt.init(Cipher.ENCRYPT_MODE, key); + if (encrypt.getIV() != null) + { + decrypt.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(encrypt.getIV())); + } + else + { + decrypt.init(Cipher.DECRYPT_MODE, key); + } + + byte[] ciphertext = encrypt.doFinal(new byte[1000]); + + // Tamper + ciphertext[0] += 1; + + InputStream input = createInputStream(ciphertext, decrypt, useBc); + try + { + while (input.read() >= 0) + { + } + fail("Expected invalid ciphertext after tamper and read : " + name, authenticated, useBc); + } + catch (InvalidCipherTextIOException e) + { + // Expected + } + try + { + input.close(); + } + catch (Exception e) + { + fail("Unexpected exception : " + name, e, authenticated, useBc); + } + } + + /** + * Test truncation of ciphertext to make tag calculation impossible, followed by read from + * decrypting CipherInputStream + */ + private void testTruncatedRead(String name, Key key, boolean authenticated, boolean useBc) + throws Exception + { + Cipher encrypt = Cipher.getInstance(name, "BC"); + Cipher decrypt = Cipher.getInstance(name, "BC"); + encrypt.init(Cipher.ENCRYPT_MODE, key); + if (encrypt.getIV() != null) + { + decrypt.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(encrypt.getIV())); + } + else + { + decrypt.init(Cipher.DECRYPT_MODE, key); + } + + byte[] ciphertext = encrypt.doFinal(new byte[1000]); + + // Truncate to just smaller than complete tag + byte[] truncated = new byte[ciphertext.length - 1000 - 1]; + System.arraycopy(ciphertext, 0, truncated, 0, truncated.length); + + // Tamper + ciphertext[0] += 1; + + InputStream input = createInputStream(truncated, decrypt, useBc); + while (true) + { + int read = 0; + try + { + read = input.read(); + } + catch (InvalidCipherTextIOException e) + { + // Expected + break; + } + catch (Exception e) + { + fail("Unexpected exception : " + name, e, authenticated, useBc); + break; + } + if (read < 0) + { + fail("Expected invalid ciphertext after truncate and read : " + name, authenticated, useBc); + break; + } + } + try + { + input.close(); + } + catch (Exception e) + { + fail("Unexpected exception : " + name, e, authenticated, useBc); + } + } + + /** + * Test tampering of ciphertext followed by write to decrypting CipherOutputStream + */ + private void testTamperedWrite(String name, Key key, boolean authenticated, boolean useBc) + throws Exception + { + Cipher encrypt = Cipher.getInstance(name, "BC"); + Cipher decrypt = Cipher.getInstance(name, "BC"); + encrypt.init(Cipher.ENCRYPT_MODE, key); + if (encrypt.getIV() != null) + { + decrypt.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(encrypt.getIV())); + } + else + { + decrypt.init(Cipher.DECRYPT_MODE, key); + } + + byte[] ciphertext = encrypt.doFinal(new byte[1000]); + + // Tamper + ciphertext[0] += 1; + + ByteArrayOutputStream plaintext = new ByteArrayOutputStream(); + OutputStream output = createOutputStream(plaintext, decrypt, useBc); + + for (int i = 0; i < ciphertext.length; i++) + { + output.write(ciphertext[i]); + } + try + { + output.close(); + fail("Expected invalid ciphertext after tamper and write : " + name, authenticated, useBc); + } + catch (InvalidCipherTextIOException e) + { + // Expected + } + } + + /** + * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE + */ + private void testWriteRead(String name, Key key, boolean authenticated, boolean useBc, boolean blocks) + throws Exception + { + byte[] data = new byte[1000]; + for (int i = 0; i < data.length; i++) + { + data[i] = (byte)(i % 255); + } + + testWriteRead(name, key, authenticated, useBc, blocks, data); + } + + /** + * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE + */ + private void testWriteReadEmpty(String name, Key key, boolean authenticated, boolean useBc, boolean blocks) + throws Exception + { + byte[] data = new byte[0]; + + testWriteRead(name, key, authenticated, useBc, blocks, data); + } + + private void testWriteRead(String name, Key key, boolean authenticated, boolean useBc, boolean blocks, byte[] data) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + Cipher encrypt = Cipher.getInstance(name, "BC"); + Cipher decrypt = Cipher.getInstance(name, "BC"); + encrypt.init(Cipher.ENCRYPT_MODE, key); + if (encrypt.getIV() != null) + { + decrypt.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(encrypt.getIV())); + } + else + { + decrypt.init(Cipher.DECRYPT_MODE, key); + } + + OutputStream cOut = createOutputStream(bOut, encrypt, useBc); + 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(); + InputStream cIn = createInputStream(cipherText, decrypt, useBc); + + if (blocks) + { + byte[] block = new byte[encrypt.getBlockSize() + 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 " + name, e, authenticated, useBc); + } + + byte[] decrypted = bOut.toByteArray(); + if (!Arrays.areEqual(data, decrypted)) + { + fail("Failed - decrypted data doesn't match: " + name, authenticated, useBc); + } + } + + 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); + } + } + + protected void fail(String message, Throwable throwable, boolean authenticated, boolean bc) + { + if (bc || !authenticated) + { + super.fail(message, throwable); + } + else + { + // javax.crypto.CipherInputStream/CipherOutputStream + // are broken wrt handling AEAD failures + //System.err.println("Broken JCE Streams: " + message + " : " + throwable); + throwable.printStackTrace(); + } + } + + /** + * Test CipherInputStream in ENCRYPT_MODE, CipherOutputStream in DECRYPT_MODE + */ + private void testReadWrite(String name, Key key, boolean authenticated, boolean useBc, boolean blocks) + throws Exception + { + String lCode = "ABCDEFGHIJKLMNOPQRSTU"; + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + Cipher in = Cipher.getInstance(name, "BC"); + Cipher out = Cipher.getInstance(name, "BC"); + in.init(Cipher.ENCRYPT_MODE, key); + if (in.getIV() != null) + { + out.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(in.getIV())); + } + else + { + out.init(Cipher.DECRYPT_MODE, key); + } + + InputStream cIn = createInputStream(lCode.getBytes(), in, useBc); + OutputStream cOut = createOutputStream(bOut, out, useBc); + + if (blocks) + { + byte[] block = new byte[in.getBlockSize() + 1]; + int c; + while ((c = cIn.read(block)) >= 0) + { + cOut.write(block, 0, c); + } + } + else + { + int c; + while ((c = cIn.read()) >= 0) + { + cOut.write(c); + } + } + + cIn.close(); + + cOut.flush(); + cOut.close(); + + } + catch (Exception e) + { + fail("Unexpected exception " + name, e, authenticated, useBc); + } + + String res = new String(bOut.toByteArray()); + if (!res.equals(lCode)) + { + fail("Failed - decrypted data doesn't match: " + name, authenticated, useBc); + } + } + + private static Key generateKey(String name) + throws Exception + { + KeyGenerator kGen; + + if (name.indexOf('/') < 0) + { + kGen = KeyGenerator.getInstance(name, "BC"); + } + else + { + kGen = KeyGenerator.getInstance(name.substring(0, name.indexOf('/')), "BC"); + } + return kGen.generateKey(); + } + + public void performTest() + throws Exception + { + final String[] blockCiphers64 = new String[]{"BLOWFISH", "DES", "DESEDE", "TEA", "CAST5", "RC2", "XTEA"}; + + for (int i = 0; i != blockCiphers64.length; i++) + { + testModes(blockCiphers64[i], new String[]{ + "/ECB/PKCS5Padding", + "/CBC/PKCS5Padding", + "/OFB/NoPadding", + "/CFB/NoPadding", + "/CTS/NoPadding",}, false); + testModes(blockCiphers64[i], new String[]{"/EAX/NoPadding"}, true); + } + + final String[] blockCiphers128 = new String[]{ + "AES", + "NOEKEON", + "Twofish", + "CAST6", + "SEED", + "Serpent", + "RC6", + "CAMELLIA"}; + + for (int i = 0; i != blockCiphers128.length; i++) + { + testModes(blockCiphers128[i], new String[]{ + "/ECB/PKCS5Padding", + "/CBC/PKCS5Padding", + "/OFB/NoPadding", + "/CFB/NoPadding", + "/CTS/NoPadding", + "/CTR/NoPadding", + "/SIC/NoPadding"}, false); + testModes(blockCiphers128[i], new String[]{"/CCM/NoPadding", "/EAX/NoPadding", "/GCM/NoPadding", "/OCB/NoPadding"}, true); + } + + final String[] streamCiphers = new String[]{ + "ARC4", + "SALSA20", + "XSalsa20", + "ChaCha", + "Grainv1", + "Grain128", + "HC128", + "HC256"}; + + for (int i = 0; i != streamCiphers.length; i++) + { + testModes(streamCiphers[i], new String[]{""}, false); + } + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + runTest(new CipherStreamTest2()); + } + +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java index c0720be..4ab21ed 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java @@ -28,6 +28,8 @@ import javax.crypto.interfaces.DHPublicKey; import javax.crypto.spec.DESKeySpec; import javax.crypto.spec.DESedeKeySpec; import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.DHPrivateKeySpec; +import javax.crypto.spec.DHPublicKeySpec; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; @@ -264,13 +266,17 @@ public class DHTest } private void testTwoParty(String algName, int size, int privateValueSize, KeyPairGenerator keyGen) + throws Exception + { + testTwoParty(algName, size, privateValueSize, keyGen.generateKeyPair(), keyGen.generateKeyPair()); + } + + private byte[] testTwoParty(String algName, int size, int privateValueSize, KeyPair aKeyPair, KeyPair bKeyPair) throws Exception { // // a side // - KeyPair aKeyPair = keyGen.generateKeyPair(); - KeyAgreement aKeyAgree = KeyAgreement.getInstance(algName, "BC"); checkKeySize(privateValueSize, aKeyPair); @@ -280,8 +286,6 @@ public class DHTest // // b side // - KeyPair bKeyPair = keyGen.generateKeyPair(); - KeyAgreement bKeyAgree = KeyAgreement.getInstance(algName, "BC"); checkKeySize(privateValueSize, bKeyPair); @@ -294,13 +298,15 @@ public class DHTest aKeyAgree.doPhase(bKeyPair.getPublic(), true); bKeyAgree.doPhase(aKeyPair.getPublic(), true); - BigInteger k1 = new BigInteger(aKeyAgree.generateSecret()); - BigInteger k2 = new BigInteger(bKeyAgree.generateSecret()); + byte[] aSecret = aKeyAgree.generateSecret(); + byte[] bSecret = bKeyAgree.generateSecret(); - if (!k1.equals(k2)) + if (!Arrays.areEqual(aSecret, bSecret)) { fail(size + " bit 2-way test failed"); } + + return aSecret; } private void testExplicitWrapping( @@ -415,7 +421,7 @@ public class DHTest new BouncyCastleProvider().setParameter(ConfigurableProvider.DH_DEFAULT_PARAMS, dhParams); - KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algName, "BC"); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algName, "BC"); keyGen.initialize(dhParams.getP().bitLength()); @@ -727,6 +733,38 @@ public class DHTest testTwoParty("DH", 512, 0, keyGen); } + private void testSmallSecret() + throws Exception + { + BigInteger p = new BigInteger("ff3b512a4cc0961fa625d6cbd9642c377ece46b8dbc3146a98e0567f944034b5e3a1406edb179a77cd2539bdb74dc819f0a74d486606e26e578ff52c5242a5ff", 16); + BigInteger g = new BigInteger("58a66667431136e99d86de8199eb650a21afc9de3dd4ef9da6dfe89c866e928698952d95e68b418becef26f23211572eebfcbf328809bdaf02bba3d24c74f8c0", 16); + + DHPrivateKeySpec aPrivSpec = new DHPrivateKeySpec( + new BigInteger("30a6ea4e2240a42867ad98bd3adbfd5b81aba48bd930f20a595983d807566f7cba4e766951efef2c6c0c1be3823f63d66e12c2a091d5ff3bbeb1ea6e335d072d", 16), p, g); + DHPublicKeySpec aPubSpec = new DHPublicKeySpec( + new BigInteger("694dfea1bfc8897e2fcbfd88033ab34f4581892d7d5cc362dc056e3d43955accda12222bd651ca31c85f008a05dea914de68828dfd83a54a340fa84f3bbe6caf", 16), p, g); + + DHPrivateKeySpec bPrivSpec = new DHPrivateKeySpec( + new BigInteger("775b1e7e162190700e2212dd8e4aaacf8a2af92c9c108b81d5bf9a14548f494eaa86a6c4844b9512eb3e3f2f22ffec44c795c813edfea13f075b99bbdebb34bd", 16), p, g); + + DHPublicKeySpec bPubSpec = new DHPublicKeySpec( + new BigInteger("d8ddd4ff9246635eadbfa0bc2ef06d98a329b6e8cd2d1435d7b4921467570e697c9a9d3c172c684626a9d2b6b2fa0fc725d5b91f9a9625b717a4169bc714b064", 16), p, g); + + KeyFactory kFact = KeyFactory.getInstance("DH", "BC"); + + byte[] secret = testTwoParty("DH", 512, 0, new KeyPair(kFact.generatePublic(aPubSpec), kFact.generatePrivate(aPrivSpec)), new KeyPair(kFact.generatePublic(bPubSpec), kFact.generatePrivate(bPrivSpec))); + + if (secret.length != ((p.bitLength() + 7) / 8)) + { + fail("short secret wrong length"); + } + + if (!Arrays.areEqual(Hex.decode("00340d3309ddc86e99e2f0be4fc212837bfb5c59336b09b9e1aeb1884b72c8b485b56723d0bf1c1d37fc89a292fc1cface9125106f1df15f55f22e4f77c5879b"), secret)) + { + fail("short secret mismatch"); + } + } + private void testEnc() throws Exception { @@ -882,6 +920,7 @@ public class DHTest testExceptions(); testDESAndDESede(g768, p768); testInitialise(); + testSmallSecret(); testConfig(); } diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DetDSATest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DetDSATest.java new file mode 100644 index 0000000..16c1969 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DetDSATest.java @@ -0,0 +1,152 @@ +package org.bouncycastle.jce.provider.test; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Security; +import java.security.Signature; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.EllipticCurve; + +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.nist.NISTNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.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 DetDSATest + 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() + throws Exception + { + DSAPrivateKeySpec privKeySpec = new DSAPrivateKeySpec(new BigInteger("411602CB19A6CCC34494D79D98EF1E7ED5AF25F7", 16), + new BigInteger("86F5CA03DCFEB225063FF830A0C769B9DD9D6153AD91D7CE27F787C43278B447" + + "E6533B86B18BED6E8A48B784A14C252C5BE0DBF60B86D6385BD2F12FB763ED88" + + "73ABFD3F5BA2E0A8C0A59082EAC056935E529DAF7C610467899C77ADEDFC846C" + + "881870B7B19B2B58F9BE0521A17002E3BDD6B86685EE90B3D9A1B02B782B1779", 16), + new BigInteger("996F967F6C8E388D9E28D01E205FBA957A5698B1", 16), + new BigInteger("07B0F92546150B62514BB771E2A0C0CE387F03BDA6C56B505209FF25FD3C133D" + + "89BBCD97E904E09114D9A7DEFDEADFC9078EA544D2E401AEECC40BB9FBBF78FD" + + "87995A10A1C27CB7789B594BA7EFB5C4326A9FE59A070E136DB77175464ADCA4" + + "17BE5DCE2F40D10A46A3A3943F26AB7FD9C0398FF8C76EE0A56826A8A88F1DBD", 16)); + + KeyFactory keyFact = KeyFactory.getInstance("DSA", "BC"); + + PrivateKey privKey = keyFact.generatePrivate(privKeySpec); + + doTestHMACDetDSASample("SHA1withDETDSA", privKey, new BigInteger("2E1A0C2562B2912CAAF89186FB0F42001585DA55", 16), new BigInteger("29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5", 16)); + doTestHMACDetDSASample("SHA224withDETDSA", privKey, new BigInteger("4BC3B686AEA70145856814A6F1BB53346F02101E", 16), new BigInteger("410697B92295D994D21EDD2F4ADA85566F6F94C1", 16)); + doTestHMACDetDSASample("SHA256withDETDSA", privKey, new BigInteger("81F2F5850BE5BC123C43F71A3033E9384611C545", 16), new BigInteger("4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89", 16)); + doTestHMACDetDSASample("SHA384withDETDSA", privKey, new BigInteger("07F2108557EE0E3921BC1774F1CA9B410B4CE65A", 16), new BigInteger("54DF70456C86FAC10FAB47C1949AB83F2C6F7595", 16)); + doTestHMACDetDSASample("SHA512withDETDSA", privKey, new BigInteger("16C3491F9B8C3FBBDD5E7A7B667057F0D8EE8E1B", 16), new BigInteger("02C36A127A7B89EDBB72E4FFBC71DABC7D4FC69C", 16)); + } + + private void doTestHMACDetDSASample(String algName, PrivateKey privKey, BigInteger r, BigInteger s) + throws Exception + { + doTestHMACDetECDSA(Signature.getInstance(algName, "BC"), SAMPLE, privKey, r, s); + } + + // test vectors from appendix in RFC 6979 + private void testECHMacDeterministic() + throws Exception + { + X9ECParameters x9ECParameters = NISTNamedCurves.getByName("P-192"); + ECCurve curve = x9ECParameters.getCurve(); + + ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(new BigInteger("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), + new ECParameterSpec( + new EllipticCurve(new ECFieldFp(((ECCurve.Fp)curve).getQ()), curve.getA().toBigInteger(), curve.getB().toBigInteger(), null), + new ECPoint(x9ECParameters.getG().getXCoord().toBigInteger(), x9ECParameters.getG().getYCoord().toBigInteger()), + x9ECParameters.getN(), x9ECParameters.getH().intValue()) + ); + + KeyFactory keyFact = KeyFactory.getInstance("ECDSA", "BC"); + + PrivateKey privKey = keyFact.generatePrivate(privKeySpec); + + doTestHMACDetECDSASample("SHA1withDETECDSA", privKey, new BigInteger("98C6BD12B23EAF5E2A2045132086BE3EB8EBD62ABF6698FF", 16), new BigInteger("57A22B07DEA9530F8DE9471B1DC6624472E8E2844BC25B64", 16)); + doTestHMACDetECDSASample("SHA224withDETECDSA", privKey, new BigInteger("A1F00DAD97AEEC91C95585F36200C65F3C01812AA60378F5", 16), new BigInteger("E07EC1304C7C6C9DEBBE980B9692668F81D4DE7922A0F97A", 16)); + doTestHMACDetECDSASample("SHA256withDETECDSA", privKey, new BigInteger("4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55", 16), new BigInteger("CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85", 16)); + doTestHMACDetECDSASample("SHA384withDETECDSA", privKey, new BigInteger("DA63BF0B9ABCF948FBB1E9167F136145F7A20426DCC287D5", 16), new BigInteger("C3AA2C960972BD7A2003A57E1C4C77F0578F8AE95E31EC5E", 16)); + doTestHMACDetECDSASample("SHA512withDETECDSA", privKey, new BigInteger("4D60C5AB1996BD848343B31C00850205E2EA6922DAC2E4B8", 16), new BigInteger("3F6E837448F027A1BF4B34E796E32A811CBB4050908D8F67", 16)); + + doTestHMACDetECDSATest("SHA1withDETECDSA", privKey, new BigInteger("0F2141A0EBBC44D2E1AF90A50EBCFCE5E197B3B7D4DE036D", 16), new BigInteger("EB18BC9E1F3D7387500CB99CF5F7C157070A8961E38700B7", 16)); + doTestHMACDetECDSATest("SHA224withDETECDSA", privKey, new BigInteger("6945A1C1D1B2206B8145548F633BB61CEF04891BAF26ED34", 16), new BigInteger("B7FB7FDFC339C0B9BD61A9F5A8EAF9BE58FC5CBA2CB15293", 16)); + doTestHMACDetECDSATest("SHA256withDETECDSA", privKey, new BigInteger("3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE", 16), new BigInteger("5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F", 16)); + doTestHMACDetECDSATest("SHA384withDETECDSA", privKey, new BigInteger("B234B60B4DB75A733E19280A7A6034BD6B1EE88AF5332367", 16), new BigInteger("7994090B2D59BB782BE57E74A44C9A1C700413F8ABEFE77A", 16)); + doTestHMACDetECDSATest("SHA512withDETECDSA", privKey, new BigInteger("FE4F4AE86A58B6507946715934FE2D8FF9D95B6B098FE739", 16), new BigInteger("74CF5605C98FBA0E1EF34D4B5A1577A7DCF59457CAE52290", 16)); + } + + private void doTestHMACDetECDSASample(String sigAlg, PrivateKey privKey, BigInteger r, BigInteger s) + throws Exception + { + doTestHMACDetECDSA(Signature.getInstance(sigAlg, "BC"), SAMPLE, privKey, r, s); + } + + private void doTestHMACDetECDSATest(String sigAlg, PrivateKey privKey, BigInteger r, BigInteger s) + throws Exception + { + doTestHMACDetECDSA(Signature.getInstance(sigAlg, "BC"), TEST, privKey, r, s); + } + + private void doTestHMACDetECDSA(Signature detSigner, byte[] data, PrivateKey privKey, BigInteger r, BigInteger s) + throws Exception + { + detSigner.initSign(privKey); + + detSigner.update(data, 0, data.length); + + byte[] m = detSigner.sign(); + + ASN1Sequence seq = ASN1Sequence.getInstance(m); + + + if (!r.equals(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue())) + { + fail("r value wrong"); + } + if (!s.equals(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue())) + { + fail("s value wrong"); + } + } + + public String getName() + { + return "DetDSA"; + } + + public void performTest() + throws Exception + { + testHMacDeterministic(); + testECHMacDeterministic(); + } + + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new DetDSATest()); + } +} + diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java index 5e37991..679f3ea 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java @@ -30,7 +30,8 @@ public class DigestTest { "RIPEMD320", "de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d" }, { "Tiger", "2AAB1484E8C158F2BFB8C5FF41B57A525129131C957B5F93" }, { "GOST3411", "b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c" }, - { "WHIRLPOOL", "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5" } + { "WHIRLPOOL", "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5" }, + { "SM3", "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0" }, }; public String getName() diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java index 0f93bdc..802134c 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java @@ -18,6 +18,7 @@ import java.security.Security; import java.security.Signature; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; +import java.security.spec.AlgorithmParameterSpec; import java.security.spec.ECFieldF2m; import java.security.spec.ECFieldFp; import java.security.spec.ECGenParameterSpec; @@ -28,6 +29,7 @@ import java.security.spec.ECPublicKeySpec; import java.security.spec.EllipticCurve; import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERInteger; @@ -38,6 +40,7 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x9.X962Parameters; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.jce.ECKeyUtil; +import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.ECPointUtil; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.BigIntegers; @@ -687,6 +690,50 @@ public class ECDSA5Test } } + private static class ECRandom + extends SecureRandom + { + public void nextBytes(byte[] bytes) + { + byte[] src = BigInteger.valueOf(1000).toByteArray(); + System.arraycopy(src, 0, bytes, bytes.length - src.length, src.length); + } + } + + private void testNamedCurveParameterPreservation() + throws Exception + { + AlgorithmParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1"); + KeyPairGenerator keygen = KeyPairGenerator.getInstance("EC", "BC"); + keygen.initialize(ecSpec, new ECRandom()); + + KeyPair keys = keygen.generateKeyPair(); + + PrivateKeyInfo priv1 = PrivateKeyInfo.getInstance(keys.getPrivate().getEncoded()); + SubjectPublicKeyInfo pub1 = SubjectPublicKeyInfo.getInstance(keys.getPublic().getEncoded()); + + keygen = KeyPairGenerator.getInstance("EC", "BC"); + keygen.initialize(new ECGenParameterSpec("secp256r1"), new ECRandom()); + + PrivateKeyInfo priv2 = PrivateKeyInfo.getInstance(keys.getPrivate().getEncoded()); + SubjectPublicKeyInfo pub2 = SubjectPublicKeyInfo.getInstance(keys.getPublic().getEncoded()); + + if (!priv1.equals(priv2) || !pub1.equals(pub2)) + { + fail("mismatch between alg param spec and ECGenParameterSpec"); + } + + if (!(priv2.getPrivateKeyAlgorithm().getParameters() instanceof ASN1ObjectIdentifier)) + { + fail("OID not preserved in private key"); + } + + if (!(pub1.getAlgorithm().getParameters() instanceof ASN1ObjectIdentifier)) + { + fail("OID not preserved in public key"); + } + } + protected BigInteger[] derDecode( byte[] encoding) throws IOException @@ -718,6 +765,7 @@ public class ECDSA5Test testECDSA239bitBinary(); testGeneration(); testKeyPairGenerationWithOIDs(); + testNamedCurveParameterPreservation(); } public static void main( diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECEncodingTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECEncodingTest.java index 9a5135a..1bd3178 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECEncodingTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECEncodingTest.java @@ -120,8 +120,8 @@ public class ECEncodingTest { ((ECPointEncoder)pubKey).setPointFormat("UNCOMPRESSED"); } - byte[] x = pubKey.getQ().getX().toBigInteger().toByteArray(); - byte[] y = pubKey.getQ().getY().toBigInteger().toByteArray(); + byte[] x = pubKey.getQ().getAffineXCoord().toBigInteger().toByteArray(); + byte[] y = pubKey.getQ().getAffineYCoord().toBigInteger().toByteArray(); if (x.length == y.length) { success = true; diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java index b7fecd0..93e3ad7 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java @@ -11,8 +11,8 @@ import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -182,11 +182,11 @@ public class GOST28147Test private void oidTest() { String[] oids = { - CryptoProObjectIdentifiers.gostR28147_cbc.getId(), + CryptoProObjectIdentifiers.gostR28147_gcfb.getId(), }; String[] names = { - "GOST28147/CBC/PKCS7Padding" + "GOST28147/GCFB/NoPadding" }; try diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java index 103a3e3..fa90108 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java @@ -122,12 +122,12 @@ public class ImplicitlyCaTest testEncoding(sKey, vKey); - ECPublicKey vKey2 = (ECPublicKey)fact.generatePublic(new ECPublicKeySpec(vKey.getQ(), ecSpec)); - ECPrivateKey sKey2 = (ECPrivateKey)fact.generatePrivate(new ECPrivateKeySpec(sKey.getD(), ecSpec)); + ECPublicKey vKey2 = (ECPublicKey)fact.generatePublic(new ECPublicKeySpec(vKey.getQ(), null)); + ECPrivateKey sKey2 = (ECPrivateKey)fact.generatePrivate(new ECPrivateKeySpec(sKey.getD(), null)); if (!vKey.equals(vKey2) || vKey.hashCode() != vKey2.hashCode()) { - fail("private equals/hashCode failed"); + fail("public equals/hashCode failed"); } if (!sKey.equals(sKey2) || sKey.hashCode() != sKey2.hashCode()) diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/OCBTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/OCBTest.java new file mode 100644 index 0000000..c693ce8 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/OCBTest.java @@ -0,0 +1,104 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.Key; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +public class OCBTest + extends SimpleTest +{ + public String getName() + { + return "OCB"; + } + + public void performTest() + throws Exception + { + checkRegistrations(); + } + + private void checkRegistrations() + throws Exception + { + String[] ciphers = new String[] { "AES", "NOEKEON", "Twofish", "CAST6", "SEED", "Serpent", "RC6", "CAMELLIA" }; + String[] cipherText = new String[] + { + "BEA5E8798DBE7110031C144DA0B2612213CC8B747807121A4CBB3E4BD6B456AF", + "a2545b927e0f2e6db2998e20b17d5fc0564dcab63b748327e2ef4eaed88cb059", + "1cfafe72f7181cae331610c116345e51fc356b379aca04da2a53337c5428d8e4", + "5b9b738b2ac7000b33b89dd4eec18dd853f4f7c1d9e17b565405f17a0a8c8b63", + "fcdbcee69d02c69858ed4569f78b81920b3027cdb7f1f154634aa5ace9e6ba29", + "4f7154cb34558940e85db7d3e96ac6c9cb0d9c1b00b18e82e15d1be83deef9df", + "23f3e450c4c7199563a0ed601a5c60d75eb88db2a0d090ae5e84d98438a146aa", + "ac13ce9db4af148e910a813fc728e5785e23b1bf1d04a961a3f95f356b9417ab" + }; + + for (int i = 0; i < ciphers.length; i++) + { + ocbTest(ciphers[i], cipherText[i]); + } + } + + private void ocbTest(String cipher, String cText) + throws Exception + { + byte[] K = Hex.decode( + "000102030405060708090A0B0C0D0E0F"); + byte[] P = Hex.decode( + "000102030405060708090A0B0C0D0E0F"); + byte[] N = Hex.decode("000102030405060708090A0B"); + String T = "4CBB3E4BD6B456AF"; + byte[] C = Hex.decode(cText); + + Key key; + Cipher in, out; + + key = new SecretKeySpec(K, cipher); + + in = Cipher.getInstance(cipher + "/OCB/NoPadding", "BC"); + out = Cipher.getInstance(cipher + "/OCB/NoPadding", "BC"); + + in.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(N)); + + byte[] enc = in.doFinal(P); + if (!areEqual(enc, C)) + { + fail("ciphertext doesn't match in OCB got " + new String(Hex.encode(enc))); + } + + out.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(N)); + + byte[] dec = out.doFinal(C); + if (!areEqual(dec, P)) + { + fail("plaintext doesn't match in OCB"); + } + + try + { + in = Cipher.getInstance(cipher + "/OCB/PKCS5Padding", "BC"); + + fail("bad padding missed in OCB"); + } + catch (NoSuchPaddingException e) + { + // expected + } + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new OCBTest()); + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java index c35c5b8..0828440 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java @@ -11,6 +11,7 @@ import java.security.KeyStore; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Security; +import java.security.Signature; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; @@ -426,6 +427,49 @@ public class PKCS12StoreTest + "AHoAeQB0AGsAbwB3AG4AaQBrAGEwMTAhMAkGBSsOAwIaBQAEFKJpUOIj0OtI" + "j2CPp38YIFBEqvjsBAi8G+yhJe3A/wICCAA="); + private byte[] gostPfx = Base64.decode( + "MIIHEgIBAzCCBssGCSqGSIb3DQEHAaCCBrwEgga4MIIGtDCCBYEGCSqGSIb3" + + "DQEHBqCCBXIwggVuAgEAMIIFZwYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBI" + + "MCcGCSqGSIb3DQEFDDAaBAi114+lRrpkXAICCAAwCgYGKoUDAgIKBQAwHQYG" + + "KoUDAgIVMBMECLEIQPMsz/ZZBgcqhQMCAh8BgIIFAbu13yJiW/BnSKYKbtv9" + + "tDJoTv6l9BVpCCI4tvpzJnMeLBJyVZU4JevcJNii+R1LilVuuB+xc8e7/P4G" + + "6TILWmnnispr9KPRAbYRfoCJOa59+TYJMur58wwDuYgMapQAFzsvpzyUWi62" + + "o3uQbbLKO9hQCeJW2L+K9cbg8k33MjXMLpnblKpqmZbHTmBJDFR3xGw7IEjD" + + "UNqruu7DlHY6jctiVJSii9UNEVetSo9AAzfROxRjROg38VsWxLyO9wEMBv/8" + + "H8ur+zOtmQPGqirNXmN+pa08OvZin9kh7CgswW03xIbfsdGGGLRAWtvCnEwJ" + + "mS2tEfH1SZcuVLpMomhq3FU/jsc12k+vq/jw4I2cmfDL41ieK72bwNj8xUXu" + + "JHeoFSPGX4z+nsJUrFbFG4VBuDs2Y0SCWLyYZvdjvJwYjfqtyi/RoFSZjGHF" + + "crstf9YNQ0vW0efCJ7pUBH44OrbnCx5ng2U5jFm1b3HBIKA2RX+Tlhv14MgT" + + "KSftPZ67eSmgdsyPuQAdMu6fEdBMpVKMNZNRV565690sqi+1jOmH94TUX8XU" + + "2pRQj6eGGLq6lgGnnDabcePUEPXW8zW2KYrDKYJ/1QZmVGldvlqnjZMNhIO+" + + "Afsqax/P8RBjMduGqdilGdRzbN8PdhVaN0Ys+WzFxiS9gtaA2yPzcQuedWDN" + + "T7sIrfIapgFYmmHRQ7ht4AKj+lmOyNadONYw+ww+8RzHB1d2Kk+iXeZCtvH0" + + "XFWJZtuoGKSt/gkI0E2vpDfMbLaczaRC7ityO0iJs25ozP4JhZRBVvOmpxc9" + + "YuIetbTnTf1TLJKXDgt1IwPZeugbofSeiNv117lx8VgtvMYFD4W+WQlB8HnO" + + "C8NOYjkMPElc6PCMB9gGm0cIu1fKLvY8ycLav93JJjdDuC0kgKLb2+8mC5+2" + + "DdMkcfgW6hy4c98xnJs8enCww3A4xkRbMU13zMq70liqmKHV2SSurg5hwUHM" + + "ZthT8p988ZBrnqW24lXfMBqTK4YtIBMeMnvKocYBXr96ig3GfahI1Aj2Bw2e" + + "bpZTVeayYUd+2xX8JJMdqna6Q61AL8/eUhJUETz5+fgQJtPjcKmdJfVHO6nB" + + "vOk1t/rjK17eiXLxHCyvfP+Tw8lSFOhcvr4eIeG8WfsWNRu2eKKosOU7uash" + + "QpnvQieqDeijuRxf+tbbJ5D86inwbJqdxra7wNuZXmiaB9gFDzNbNjhtL+6i" + + "gUyX/iQHKi9bNK+PH6pdH/gkwnG/juhdgqoNY6GRty/LUOPgXD+r5e/ST16R" + + "vnlwrlKp5FzRWBEkem+dhelj3rb+cxKEyvPe3TvIUFcmIlV1VCRQ1fBHtX18" + + "eC3a3GprH8c40z3S/kdyk7GlFQ27DRLka+iDN05b+MP5jlgvfqYBKxwLfeNu" + + "MpxWoCUvYWiQdMih86/l0H+0o5UB8SqRbpuvr6fY910JCk0hDaO1pgB3HlRz" + + "k1vb46pg25heXQm3JmO+ghxjOGliYBWjl8p7AfRS9cjS8ca+X02Mv9Viv7Ce" + + "3+Gz0MVwfK98viJ3CFxkaEBlM2LM0IeUQbkHG+YwYaTSfl4GYyrug4F0ZdrA" + + "KeY9/kIxa/OJxjcIMs2H+2mSpxmrb7ylmHZ2RB8ITiduRVtO091hn/J7N+eT" + + "h6BvLBKIFU+UFUdgjxoDNDk7ao++Mu9T3dQfceFBOYzW9vMQgX30yaPLSdan" + + "ZMAP0VtiNjCCASsGCSqGSIb3DQEHAaCCARwEggEYMIIBFDCCARAGCyqGSIb3" + + "DQEMCgECoIGyMIGvMFUGCSqGSIb3DQEFDTBIMCcGCSqGSIb3DQEFDDAaBAiQ" + + "Owewo16xzQICCAAwCgYGKoUDAgIKBQAwHQYGKoUDAgIVMBMECHSCNJJcQ2VI" + + "BgcqhQMCAh8BBFYCyRRpFtZgnsxeK7ZHT+aOyoVmzhtnLrqoBHgV4nJJW2/e" + + "UcJjc2Rlbzfd+3L/GWcRGF8Bgn+MjiaAqE64Rzaao9t2hc3myw1WrCfPnoEx" + + "VI7OPBM5FzFMMCMGCSqGSIb3DQEJFTEWBBTV7LvI27QWRmHD45X2WKXYs3ct" + + "AzAlBgkqhkiG9w0BCRQxGB4WAGMAcABfAGUAeABwAG8AcgB0AGUAZDA+MC4w" + + "CgYGKoUDAgIJBQAEIJbGZorQsNM63+xozwEI561cTFVCbyHAEEpkvF3eijT8" + + "BAgY5sDtkrVeBQICCAA="); + /** * we generate a self signed certificate for the sake of testing - RSA */ @@ -482,6 +526,38 @@ public class PKCS12StoreTest return certGen.generate(privKey); } + private void testGOSTStore() + throws Exception + { + byte[] data = Hex.decode("deadbeef"); + + KeyStore pkcs12 = KeyStore.getInstance("PKCS12", "BC"); + + pkcs12.load(new ByteArrayInputStream(gostPfx), "1".toCharArray()); + + PrivateKey pk = (PrivateKey)pkcs12.getKey("cp_exported", null); + Certificate[] pubCerts = pkcs12.getCertificateChain("cp_exported"); + + Signature sig = Signature.getInstance("ECGOST3410", "BC"); + + sig.initSign(pk); + + sig.update(data); + + byte[] signature = sig.sign(); + + sig = Signature.getInstance("ECGOST3410", "BC"); + + sig.initVerify(pubCerts[0].getPublicKey()); + + sig.update(data); + + if (!sig.verify(signature)) + { + fail("key test failed in GOST store"); + } + } + public void testPKCS12Store() throws Exception { @@ -1080,7 +1156,7 @@ public class PKCS12StoreTest throws Exception { testPKCS12Store(); - + testGOSTStore(); // converter tests diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java new file mode 100644 index 0000000..c147c17 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java @@ -0,0 +1,150 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.NoSuchAlgorithmException; +import java.security.Security; +import java.util.ArrayList; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.crypto.generators.Poly1305KeyGenerator; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; +import org.bouncycastle.util.test.TestFailedException; + +public class Poly1305Test + extends SimpleTest +{ + private static final byte[] MASTER_KEY = Hex + .decode("95cc0e44d0b79a8856afcae1bec4fe3c01bcb20bfc8b6e03609ddd09f44b060f"); + + public String getName() + { + return "Poly1305"; + } + + public void performTest() + throws Exception + { + checkRegistrations(); + } + + private void checkRegistrations() + throws Exception + { + List missingMacs = new ArrayList(); + List missingKeyGens = new ArrayList(); + + String[] ciphers = new String[]{"AES", "NOEKEON", "Twofish", "CAST6", "SEED", "Serpent", "RC6", "CAMELLIA"}; + String[] macs = new String[]{ + "4bb5e21dd13001ed5faccfcfdaf8a854", + "6d601be3d5ebbb9972a64ed3223d913d", + "211195296d9afc7b35a1223a79487c87", + "f328857a1b653684e73760c804c55b1d", + "21cd8adb23ca84eb4dbb12780595bf28", + "211195296d9afc7b35a1223a79487c87", + "db86de7b1fcae429753d68b1263d7ca0", + "11918174f33a2f278fb86554da094112"}; + + for (int i = 0; i < ciphers.length; i++) + { + String cipherName = ciphers[i]; + Cipher cipher; + try + { + cipher = Cipher.getInstance(cipherName, "BC"); + } catch (Exception e) + { + System.err.println(cipherName + ": " + e.getMessage()); + continue; + } + int blocksize; + try + { + blocksize = cipher.getBlockSize(); + } catch (Exception e) + { + System.err.println(cipherName + ": " + e.getMessage()); + continue; + } + // Poly1305 is defined over 128 bit block ciphers + if (blocksize == 16) + { + String macName = "Poly1305-" + cipherName; + String macNameAlt = "Poly1305" + cipherName; + + // Check we have a Poly1305 registered for each name + checkMac(macName, missingMacs, missingKeyGens, macs[i]); + checkMac(macNameAlt, missingMacs, missingKeyGens, macs[i]); + } + } + if (missingMacs.size() != 0) + { + fail("Did not find Poly1305 registrations for the following ciphers: " + missingMacs); + } + if (missingKeyGens.size() != 0) + { + fail("Did not find Poly1305 KeyGenerator registrations for the following macs: " + missingKeyGens); + } + } + + private void checkMac(String name, List missingMacs, List missingKeyGens, String macOutput) + { + try + { + try + { + KeyGenerator kg = KeyGenerator.getInstance(name); + SecretKey key = kg.generateKey(); + + try + { + Poly1305KeyGenerator.checkKey(key.getEncoded()); + } catch (IllegalArgumentException e) + { + fail("Generated key for algo " + name + " does not match required Poly1305 format."); + } + + try + { + Mac mac = Mac.getInstance(name); + mac.init(new SecretKeySpec(MASTER_KEY, name), new IvParameterSpec(new byte[16])); + mac.update(new byte[128]); + byte[] bytes = mac.doFinal(); + + if (!Arrays.areEqual(bytes, Hex.decode(macOutput))) + { + fail("wrong mac value computed for " + name, macOutput, new String(Hex.encode(bytes))); + } + } catch (NoSuchAlgorithmException e) + { + missingMacs.add(name); + } + + } catch (NoSuchAlgorithmException e) + { + missingKeyGens.add(name); + } + } catch (TestFailedException e) + { + throw e; + } catch (Exception e) + { + fail("Unexpected error", e); + } + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new Poly1305Test()); + } +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RSATest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RSATest.java index a0a0572..c1f4582 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RSATest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RSATest.java @@ -43,6 +43,7 @@ import org.bouncycastle.asn1.pkcs.RSAESOAEPparams; import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.DigestInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.Arrays; @@ -646,6 +647,29 @@ public class RSATest fail("public key hashCode check failed"); } + // + // test an OAEP key + // + SubjectPublicKeyInfo oaepKey = new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, new RSAESOAEPparams()), + SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()).parsePublicKey()); + + copyKey = (RSAPublicKey)serializeDeserialize(keyFact.generatePublic(new X509EncodedKeySpec(oaepKey.getEncoded()))); + + if (!pubKey.equals(copyKey)) + { + fail("public key equality check failed"); + } + + if (pubKey.hashCode() != copyKey.hashCode()) + { + fail("public key hashCode check failed"); + } + + if (!Arrays.areEqual(copyKey.getEncoded(), oaepKey.getEncoded())) + { + fail("encoding does not match"); + } + oaepCompatibilityTest("SHA-1", priv2048Key, pub2048Key); oaepCompatibilityTest("SHA-224", priv2048Key, pub2048Key); oaepCompatibilityTest("SHA-256", priv2048Key, pub2048Key); diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java index 85972a0..e98330e 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java @@ -12,6 +12,7 @@ public class RegressionTest new FIPSDESTest(), new DESedeTest(), new AESTest(), + new AEADTest(), new CamelliaTest(), new SEEDTest(), new AESSICTest(), @@ -44,6 +45,7 @@ public class RegressionTest new WrapTest(), new DoFinalTest(), new CipherStreamTest(), + new CipherStreamTest2(), new NamedCurveTest(), new PKIXTest(), new NetscapeCertRequestTest(), @@ -68,10 +70,15 @@ public class RegressionTest new MQVTest(), new CMacTest(), new GMacTest(), + new OCBTest(), new DSTU4145Test(), new CRL5Test(), + new Poly1305Test(), new SipHashTest(), - new SHA3Test() + new SHA3Test(), + new SkeinTest(), + new Shacal2Test(), + new DetDSATest() }; public static void main( diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SerialisationTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SerialisationTest.java index 3db5e98..6d04f97 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SerialisationTest.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SerialisationTest.java @@ -1,12 +1,5 @@ package org.bouncycastle.jce.provider.test; -import org.bouncycastle.jce.interfaces.ElGamalPrivateKey; -import org.bouncycastle.jce.interfaces.ElGamalPublicKey; -import org.bouncycastle.util.encoders.Base64; -import org.bouncycastle.util.test.SimpleTest; - -import javax.crypto.interfaces.DHPrivateKey; -import javax.crypto.interfaces.DHPublicKey; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.ObjectInputStream; @@ -16,6 +9,14 @@ import java.security.interfaces.DSAPublicKey; import java.security.interfaces.RSAPrivateCrtKey; import java.security.interfaces.RSAPublicKey; +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; + +import org.bouncycastle.jce.interfaces.ElGamalPrivateKey; +import org.bouncycastle.jce.interfaces.ElGamalPublicKey; +import org.bouncycastle.util.encoders.Base64; +import org.bouncycastle.util.test.SimpleTest; + public class SerialisationTest extends SimpleTest { @@ -56,6 +57,11 @@ public class SerialisationTest + "eHNxAH4ABv///////////////v////4AAAABdXEAfgAKAAAAEMDh3xza3MJ4XNak/35BYPt4c3EAfgAG///////////////" + "+/////gAAAAF1cQB+AAoAAAADAQABeA=="); + private static byte[] rsaPub2 = Base64.decode( + "rO0ABXNyAD5vcmcuYm91bmN5Y2FzdGxlLmpjYWpjZS5wcm92aWRlci5hc3ltbWV0cmljLnJzYS5CQ1JTQVB1YmxpY0tleS" + + "Uiag5b+myEAgACTAAHbW9kdWx1c3QAFkxqYXZhL21hdGgvQmlnSW50ZWdlcjtMAA5wdWJsaWNFeHBvbmVudHEAfgABeHBz" + + "cgAUamF2YS5tYXRoLkJpZ0ludGVnZXKM/J8fqTv7HQMABkkACGJpdENvdW50SQAJYml0TGVuZ3RoSQATZmlyc3ROb256ZXJvQnl0ZU51bUkADGxvd2VzdFNldEJpdEkABnNpZ251bVsACW1hZ25pdHVkZXQAAltCeHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhw///////////////+/////gAAAAF1cgACW0Ks8xf4BghU4AIAAHhwAAAAIJqU1y+8k0wz3jgpbPO4fYxeQpuJbXQ4TMZBv0H0wz9PeHNxAH4AA////////////////v////4AAAABdXEAfgAHAAAAAwEAAXg="); + private static BigInteger elGamalY = new BigInteger("89822212135401014750127909969755994242838935150891306006689219384134393835581"); private static BigInteger elGamalX = new BigInteger("23522982289275336984843296896007818700866293719703239515258104457243931686357"); private static BigInteger elGamalG = new BigInteger("29672625807664138507782226105202719390719480236799714903174779490259822385963"); @@ -167,6 +173,17 @@ public class SerialisationTest fail("public key exponent mismatch"); } + RSAPublicKey pub2 = (RSAPublicKey)readObject(rsaPub2); + + if (!mod.equals(pub2.getModulus())) + { + fail("public key 2 modulus mismatch"); + } + if (!pubExp.equals(pub2.getPublicExponent())) + { + fail("public key 2 exponent mismatch"); + } + RSAPrivateCrtKey priv = (RSAPrivateCrtKey)readObject(rsaPriv); if (!mod.equals(priv.getModulus())) diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Shacal2Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Shacal2Test.java new file mode 100644 index 0000000..4b4954a --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Shacal2Test.java @@ -0,0 +1,154 @@ +package org.bouncycastle.jce.provider.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.security.Key; +import java.security.Security; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +/** + * basic test class for the Shacal2 cipher, vector from NESSIE (Test vectors set 8, vector# 0) + */ +public class Shacal2Test + extends SimpleTest +{ + static String[] cipherTests = + { + "512", + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F", + "98BCC10405AB0BFC686BECECAAD01AC19B452511BCEB9CB094F905C51CA45430", + "00112233445566778899AABBCCDDEEFF102132435465768798A9BACBDCEDFE0F", + + }; + + public String getName() + { + return "Shacal2"; + } + + public void testECB( + int strength, + byte[] keyBytes, + byte[] input, + byte[] output) + throws Exception + { + Key key; + Cipher in, out; + CipherInputStream cIn; + CipherOutputStream cOut; + ByteArrayInputStream bIn; + ByteArrayOutputStream bOut; + + key = new SecretKeySpec(keyBytes, "Shacal2"); + + in = Cipher.getInstance("Shacal2/ECB/NoPadding", "BC"); + out = Cipher.getInstance("Shacal2/ECB/NoPadding", "BC"); + try + { + out.init(Cipher.ENCRYPT_MODE, key); + } + catch (Exception e) + { + fail("Shacal2 failed initialisation - " + e.toString(), e); + } + + try + { + in.init(Cipher.DECRYPT_MODE, key); + } + catch (Exception e) + { + fail("Shacal2 failed initialisation - " + e.toString(), e); + } + + // + // encryption pass + // + bOut = new ByteArrayOutputStream(); + + cOut = new CipherOutputStream(bOut, out); + + try + { + for (int i = 0; i != input.length / 2; i++) + { + cOut.write(input[i]); + } + cOut.write(input, input.length / 2, input.length - input.length / 2); + cOut.close(); + } + catch (IOException e) + { + fail("Shacal2 failed encryption - " + e.toString(), e); + } + + byte[] bytes; + + bytes = bOut.toByteArray(); + + if (!areEqual(bytes, output)) + { + fail("Shacal2 failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes))); + } + + // + // decryption pass + // + bIn = new ByteArrayInputStream(bytes); + + cIn = new CipherInputStream(bIn, in); + + try + { + DataInputStream dIn = new DataInputStream(cIn); + + bytes = new byte[input.length]; + + for (int i = 0; i != input.length / 2; i++) + { + bytes[i] = (byte)dIn.read(); + } + dIn.readFully(bytes, input.length / 2, bytes.length - input.length / 2); + } + catch (Exception e) + { + fail("Shacal2 failed encryption - " + e.toString(), e); + } + + if (!areEqual(bytes, input)) + { + fail("Shacal2 failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes))); + } + } + + public void performTest() + throws Exception + { + for (int i = 0; i != cipherTests.length; i += 4) + { + testECB(Integer.parseInt(cipherTests[i]), + Hex.decode(cipherTests[i + 1]), + Hex.decode(cipherTests[i + 2]), + Hex.decode(cipherTests[i + 3])); + } + } + + public static void main( + String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new Shacal2Test()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SkeinTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SkeinTest.java new file mode 100644 index 0000000..5bc5387 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SkeinTest.java @@ -0,0 +1,316 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.MessageDigest; +import java.security.Security; + +import javax.crypto.KeyGenerator; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import org.bouncycastle.jcajce.spec.SkeinParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +public class SkeinTest + extends SimpleTest +{ + final static String provider = "BC"; + + static private byte[] nullMsg = new byte[0]; + + static private String[][] nullVectors = + { + { "Skein-256-128", "07e8ff2191c5052e1a25914c7c213078" }, + { "Skein-256-160", "ff800bed6d2044ee9d604a674e3fda50d9b24a72" }, + { "Skein-256-224", "0fadf1fa39e3837a95b3660b4184d9c2f3cfc94b55d8e7a083278bf8" }, + { "Skein-256-256", "c8877087da56e072870daa843f176e9453115929094c3a40c463a196c29bf7ba" }, + { "Skein-512-128", "7c9aff5c3738e3faadc7a5265768def1" }, + { "Skein-512-160", "49daf1ccebb3544bc93cb5019ba91b0eea8876ee" }, + { "Skein-512-224", "1541ae9fc3ebe24eb758ccb1fd60c2c31a9ebfe65b220086e7819e25" }, + { "Skein-512-256", "39ccc4554a8b31853b9de7a1fe638a24cce6b35a55f2431009e18780335d2621" }, + { "Skein-512-384", "dd5aaf4589dc227bd1eb7bc68771f5baeaa3586ef6c7680167a023ec8ce26980f06c4082c488b4ac9ef313f8cbe70808" }, + { "Skein-512-512", "bc5b4c50925519c290cc634277ae3d6257212395cba733bbad37a4af0fa06af41fca7903d06564fea7a2d3730dbdb80c1f85562dfcc070334ea4d1d9e72cba7a" }, + { "Skein-1024-384", "1fdb081963b960e89eaa11b87dda55e8a55a3e1066b30e38d8ae2a45242f7dadfaf06d80ca8a73cd8242ce5eab84c164" }, + { "Skein-1024-512", "e2943eb0bc0efabd49503a76edf7cfcf072db25bad94ed44fe537284163f3119c47ac6f78699b4272255966e0aba65c75a0a64bd23df6996d1bc3174afd9fa8b" }, + { "Skein-1024-1024", "0fff9563bb3279289227ac77d319b6fff8d7e9f09da1247b72a0a265cd6d2a62645ad547ed8193db48cff847c06494a03f55666d3b47eb4c20456c9373c86297d630d5578ebd34cb40991578f9f52b18003efa35d3da6553ff35db91b81ab890bec1b189b7f52cb2a783ebb7d823d725b0b4a71f6824e88f68f982eefc6d19c6" }, + }; + + static private byte[] shortMsg = Hex.decode("fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8" + + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb" + + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a" + + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410"); + + static private String[][] shortVectors = + { + { "Skein-256-128", "9703382ea27dc2913e9d02cd976c582f" }, + { "Skein-256-160", "0cd491b7715704c3a15a45a1ca8d93f8f646d3a1" }, + { "Skein-256-224", "afd1e2d0f5b6cd4e1f8b3935fa2497d27ee97e72060adac099543487" }, + { "Skein-256-256", "4de6fe2bfdaa3717a4261030ef0e044ced9225d066354610842a24a3eafd1dcf" }, + { "Skein-512-128", "c901b1c04af3da4dce05d7975c419224" }, + { "Skein-512-160", "ef03079d61b57c6047e15fa2b35b46fa24279539" }, + { "Skein-512-224", "d9e3219b214e15246a2038f76a573e018ef69b385b3bd0576b558231" }, + { "Skein-512-256", "809dd3f763a11af90912bbb92bc0d94361cbadab10142992000c88b4ceb88648" }, + { "Skein-512-384", "825f5cbd5da8807a7b4d3e7bd9cd089ca3a256bcc064cd73a9355bf3ae67f2bf93ac7074b3b19907a0665ba3a878b262" }, + { "Skein-512-512", "1a0d5abf4432e7c612d658f8dcfa35b0d1ab68b8d6bd4dd115c23cc57b5c5bcdde9bff0ece4208596e499f211bc07594d0cb6f3c12b0e110174b2a9b4b2cb6a9" }, + { "Skein-1024-384", "9c3d0648c11f31c18395d5e6c8ebd73f43d189843fc45235e2c35e345e12d62bc21a41f65896ddc6a04969654c2e2ce9" }, + { "Skein-1024-512", "5d0416f49c2d08dfd40a1446169dc6a1d516e23b8b853be4933513051de8d5c26baccffb08d3b16516ba3c6ccf3e9a6c78fff6ef955f2dbc56e1459a7cdba9a5" }, + { "Skein-1024-1024", "96ca81f586c825d0360aef5acaec49ad55289e1797072eee198b64f349ce65b6e6ed804fe38f05135fe769cc56240ddda5098f620865ce4a4278c77fa2ec6bc31c0f354ca78c7ca81665bfcc5dc54258c3b8310ed421d9157f36c093814d9b25103d83e0ddd89c52d0050e13a64c6140e6388431961685734b1f138fe2243086" }, + }; + + static private String[][] shortMacVectors = + { + { "Skein-Mac-256-128", "738f8b23541d50f691ab60af664c1583" }, + { "Skein-Mac-256-160", "fe07fe50f99b7683bc16980041d8c045857f1189" }, + { "Skein-Mac-256-224", "0bc19b185f5bfe50f0dba7ab49cd8ca9440260edd5a392d4bdcd2216" }, + { "Skein-Mac-256-256", "9837ba53d23afcdabd9fcd614ce9e51c0ebecec7a210df4d3724ed591f026ef1" }, + { "Skein-Mac-512-128", "6d34f46f2033947da7a9dfb068f4102d" }, + { "Skein-Mac-512-160", "83cb2effecaa60674c2f9fb2fb6771a9899708ba" }, + { "Skein-Mac-512-224", "e5f83c032875451f31977cd649c866708cb283a509e99cdfd4d995c5" }, + { "Skein-Mac-512-256", "ed5507ec551ec944c6ed531990c32907eca885dd3af3d50dd09f1dbef422bb11" }, + { "Skein-Mac-512-384", "b8f84a212723b92a591d6dc145c1655c70df710e9f3365064abdf79e9288dced2f0f895d81f465c811f1207b43b8cfce" }, + { "Skein-Mac-512-512", "d13ba582467096a0f862114d97baa218512f39c82c984aa29deee724950d7f0929f726173dd42bc35566b0dbfbf5d2a1552ba6f132de301846714215b64e7f82" }, + { "Skein-Mac-1024-384", "490dbbd049403e602ee3535181a70ee2eb5ade6d83b519953dd0d93c45729f098b679efcd64b5e3f03cd2fa9f1e70d69" }, + { "Skein-Mac-1024-512", "ce7f1052fa486309d73058d1d4986f886d966a849c72d196bb2b97fc9fb0b1e69f43a521ebd979f5a5581bd12a0dbd0d1ee27af0929881f1d35c875cc0542ecf" }, + { "Skein-Mac-1024-1024", "60cd8c755b331bcefe97be5a9fe6f63146d12520ca7b20dbc5c5370dae2ff9815c95fab564329a01eced76f0ecb1944ad52a74e89fa1b6cdcdcee4c71c2c18909c4d1324d279fac5ca2280eea0fa70521cf4ea8c616a3ac6082c2244bec5c1ab3a173faf29d84bec7fb852e278ed57785535c979b33b81465c437cd998c04b95" }, + }; + + static private String[][] shortHMacVectors = + { + { "HMAC-Skein-256-128", "926a445d5218605286dfe0542a437012" }, + { "HMAC-Skein-256-160", "5ebc30295e4562a879f94db531ada465073b8bb7" }, + { "HMAC-Skein-256-224", "a05b3cfc6b86fda7f5dcf0afbb707dc745fa55279a3f80e2c9977ff1" }, + { "HMAC-Skein-256-256", "51741f6e8ebf133216ac8e05c7a75a6339351fd2dcc4db04e418521c628a2111" }, + { "HMAC-Skein-512-128", "ad51f8c7b1b347fe52f0f5c71ae9b8eb" }, + { "HMAC-Skein-512-160", "e0d06c2d406f32bb14dbb2129176219b62d4f89f" }, + { "HMAC-Skein-512-224", "e7e5327e2aaa88d0038049e8112db31df223be4c31da24abf03731a8" }, + { "HMAC-Skein-512-256", "30177414f6e35019cacc2e3ae474b25765e6e0e541e16d754c3dad19df763ab0" }, + { "HMAC-Skein-512-384", "7f0ba3c1c642cf09eb03d0e3760fe172f22fb263006b1fba5bdea1bfaf6e971c17e039abb0030d1a40ac94a747732cce" }, + { "HMAC-Skein-512-512", "70d864e7f6cbd446778914a951d1961e646ee17a3da8eae551d29f4fafc540b0457cc9f8064c511b80dc29f8369fb5dc258559542abb5342c4892f22934bf5f1" }, + { "HMAC-Skein-1024-384", "e7d3465b30b5089e24244e747a91f7cb255596b49843466497c07e120c5c2232f51151b185a1e8a5610f041a85cc59ee" }, + { "HMAC-Skein-1024-512", "c428059ae2d17ba13e461384c4a64cb0be694909e7a04e4983a4fc16476d644c7764e0019b33ea2a8719f731a579f4f7015da7ec1bc56a4920071ac41da836fe" }, + { "HMAC-Skein-1024-1024", "3ebd13ec7bf1533c343ac78e1b5146225ce7629787f3997b646139c1b80d6f54cd562b7625419ede8710d76410dfb8617514ca3f7abf17657d2bc96722071adb2a6ecd9795a1ef5e4734b450d588efcbc3220faf53c880e61438bb953e024e48db6a745d2368375ac792be858cd01915e28590d4d6d599be95f6e6ceed7d7d91" }, + }; + + static private byte[] shortMacMessage = Hex.decode("d3090c72167517f7"); + static private byte[] shortMacKey = Hex.decode("cb41f1706cde09651203c2d0efbaddf8"); + + static private byte[] keyIdentifier = "asecretkey".getBytes(); + static private byte[] keyIdentifierVector = Hex.decode("ca9970a83997e1c346c4348b54cfc9ba7e19bfba"); + + public String getName() + { + return "Skein"; + } + + void test(String type, String algorithm, byte[] message, String expected) throws Exception + { + MessageDigest digest = MessageDigest.getInstance(algorithm, provider); + + byte[] result = digest.digest(message); + byte[] result2 = digest.digest(message); + + // test zero results valid + if (!MessageDigest.isEqual(result, Hex.decode(expected))) + { + fail(type + " result not equal for " + algorithm, expected, new String(Hex.encode(result))); + } + + // test one digest the same message with the same instance + if (!MessageDigest.isEqual(result, result2)) + { + fail(type + " result object 1 not equal"); + } + + if (!MessageDigest.isEqual(result, Hex.decode(expected))) + { + fail(type + " result object 1 not equal"); + } + + // test two, single byte updates + for (int i = 0; i < message.length; i++) + { + digest.update(message[i]); + } + result2 = digest.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail(type + " result object 2 not equal"); + } + + // test three, two half updates + digest.update(message, 0, message.length / 2); + digest.update(message, message.length / 2, message.length - message.length / 2); + result2 = digest.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail(type + " result object 3 not equal"); + } + + // test four, clone test + digest.update(message, 0, message.length / 2); + MessageDigest d = (MessageDigest)digest.clone(); + digest.update(message, message.length / 2, message.length - message.length / 2); + result2 = digest.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail(type + " result object 4(a) not equal"); + } + + d.update(message, message.length / 2, message.length - message.length / 2); + result2 = d.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail(type + " result object 4(b) not equal"); + } + + // test five, check reset() method + digest.update(message, 0, message.length / 2); + digest.reset(); + digest.update(message, 0, message.length / 2); + digest.update(message, message.length / 2, message.length - message.length / 2); + result2 = digest.digest(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail(type + " result object 5 not equal"); + } + } + + private void testMac(String algorithm, byte[] message, byte[] key, String expected) throws Exception + { + Mac mac = Mac.getInstance(algorithm, provider); + + mac.init(new SecretKeySpec(key, algorithm)); + + byte[] result = mac.doFinal(message); + byte[] result2 = mac.doFinal(message); + + // test zero results valid + if (!MessageDigest.isEqual(result, Hex.decode(expected))) + { + fail("null result not equal for " + algorithm, expected, new String(Hex.encode(result))); + } + + // test one digest the same message with the same instance + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 1 not equal"); + } + + if (!MessageDigest.isEqual(result, Hex.decode(expected))) + { + fail("Result object 1 not equal"); + } + + // test two, single byte updates + for (int i = 0; i < message.length; i++) + { + mac.update(message[i]); + } + result2 = mac.doFinal(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 2 not equal"); + } + + // test three, two half updates + mac.update(message, 0, message.length / 2); + mac.update(message, message.length / 2, message.length - message.length / 2); + result2 = mac.doFinal(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 3 not equal"); + } + + // test five, check reset() method + mac.update(message, 0, message.length / 2); + mac.reset(); + mac.update(message, 0, message.length / 2); + mac.update(message, message.length / 2, message.length - message.length / 2); + result2 = mac.doFinal(); + + if (!MessageDigest.isEqual(result, result2)) + { + fail("Result object 5 not equal"); + } + + // test six, check KeyGenerator + KeyGenerator generator = KeyGenerator.getInstance(algorithm, provider); + + mac = Mac.getInstance(algorithm, provider); + final SecretKey generatedKey = generator.generateKey(); + if (generatedKey.getEncoded().length != mac.getMacLength()) + { + fail("Default mac key length for " + algorithm); + } + mac.init(generatedKey); + mac.update(message); + mac.doFinal(); + } + + private void testParameters() throws Exception + { + Mac mac = Mac.getInstance("Skein-Mac-512-160", provider); + + // test six, init using SkeinParameters + mac.init(new SecretKeySpec(shortMacKey, "Skein-Mac-512-160"), + new SkeinParameterSpec.Builder().setKeyIdentifier(keyIdentifier).build()); + byte[] result = mac.doFinal(shortMacMessage); + + if (!MessageDigest.isEqual(result, keyIdentifierVector)) + { + fail("Mac with key identifier failed.", new String(Hex.encode(keyIdentifierVector)), new String(Hex.encode(result))); + } + } + + private void testMacKeyGenerators(String algorithm) throws Exception + { + KeyGenerator gen = KeyGenerator.getInstance(algorithm); + + int outputSize = Integer.parseInt(algorithm.substring(algorithm.lastIndexOf('-') + 1)); + SecretKey key = gen.generateKey(); + + if (key.getEncoded().length != (outputSize / 8)) { + fail(algorithm + " key length should be equal to output size " + (outputSize) + ", but was " + key.getEncoded().length * 8); + } + } + + public void performTest() throws Exception + { + for (int i = 0; i < nullVectors.length; i++) + { + test("Null message", nullVectors[i][0], nullMsg, nullVectors[i][1]); + } + for (int i = 0; i < shortVectors.length; i++) + { + test("Short message", shortVectors[i][0], shortMsg, shortVectors[i][1]); + } + for (int i = 0; i < shortMacVectors.length; i++) + { + testMac(shortMacVectors[i][0], shortMacMessage, shortMacKey, shortMacVectors[i][1]); + testMacKeyGenerators(shortMacVectors[i][0]); + } + + for (int i = 0; i < shortHMacVectors.length; i++) + { + testMac(shortHMacVectors[i][0], shortMacMessage, shortMacKey, shortHMacVectors[i][1]); + testMacKeyGenerators(shortHMacVectors[i][0]); + } + testParameters(); + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new SkeinTest()); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java index 84ebf70..b3d239e 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java @@ -49,7 +49,8 @@ public class ECNamedCurveSpec private static ECPoint convertPoint( org.bouncycastle.math.ec.ECPoint g) { - return new ECPoint(g.getX().toBigInteger(), g.getY().toBigInteger()); + g = g.normalize(); + return new ECPoint(g.getAffineXCoord().toBigInteger(), g.getAffineYCoord().toBigInteger()); } public ECNamedCurveSpec( diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECParameterSpec.java index e774a11..df91412 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECParameterSpec.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECParameterSpec.java @@ -24,7 +24,7 @@ public class ECParameterSpec BigInteger n) { this.curve = curve; - this.G = G; + this.G = G.normalize(); this.n = n; this.h = BigInteger.valueOf(1); this.seed = null; @@ -37,7 +37,7 @@ public class ECParameterSpec BigInteger h) { this.curve = curve; - this.G = G; + this.G = G.normalize(); this.n = n; this.h = h; this.seed = null; @@ -51,7 +51,7 @@ public class ECParameterSpec byte[] seed) { this.curve = curve; - this.G = G; + this.G = G.normalize(); this.n = n; this.h = h; this.seed = seed; diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECPublicKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECPublicKeySpec.java index debab00..0e21a5b 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECPublicKeySpec.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECPublicKeySpec.java @@ -22,7 +22,14 @@ public class ECPublicKeySpec { super(spec); - this.q = q; + if (q.getCurve() != null) + { + this.q = q.normalize(); + } + else + { + this.q = q; + } } /** diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java index 384d871..d03fbfe 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java @@ -1,73 +1,48 @@ -package org.bouncycastle.jce.spec; - -import java.security.spec.AlgorithmParameterSpec; - -import org.bouncycastle.crypto.engines.GOST28147Engine; +package org.bouncycastle.jce.spec; /** * A parameter spec for the GOST-28147 cipher. + * @deprecated use org.bouncycastle.jcajce.spec.GOST28147ParameterSpec */ public class GOST28147ParameterSpec - implements AlgorithmParameterSpec + extends org.bouncycastle.jcajce.spec.GOST28147ParameterSpec { - private byte[] iv = null; - private byte[] sBox = null; - + /** + * @deprecated + */ public GOST28147ParameterSpec( byte[] sBox) { - this.sBox = new byte[sBox.length]; - - System.arraycopy(sBox, 0, this.sBox, 0, sBox.length); + super(sBox); } + /** + * @deprecated + */ public GOST28147ParameterSpec( byte[] sBox, byte[] iv) { - this(sBox); - this.iv = new byte[iv.length]; - - System.arraycopy(iv, 0, this.iv, 0, iv.length); + super(sBox, iv); + } - + + /** + * @deprecated + */ public GOST28147ParameterSpec( String sBoxName) { - this.sBox = GOST28147Engine.getSBox(sBoxName); + super(sBoxName); } + /** + * @deprecated + */ public GOST28147ParameterSpec( String sBoxName, byte[] iv) { - this(sBoxName); - this.iv = new byte[iv.length]; - - System.arraycopy(iv, 0, this.iv, 0, iv.length); - } - - public byte[] getSbox() - { - return sBox; - } - - /** - * Returns the IV or null if this parameter set does not contain an IV. - * - * @return the IV or null if this parameter set does not contain an IV. - */ - public byte[] getIV() - { - if (iv == null) - { - return null; - } - - byte[] tmp = new byte[iv.length]; - - System.arraycopy(iv, 0, tmp, 0, tmp.length); - - return tmp; + super(sBoxName, iv); } }
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java index 2a7ceb5..4111072 100644 --- a/bcprov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java +++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java @@ -1,34 +1,17 @@ package org.bouncycastle.jce.spec; - -import javax.crypto.SecretKey; - /** * A simple object to indicate that a symmetric cipher should reuse the * last key provided. + * @deprecated use super class org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec */ public class RepeatedSecretKeySpec - implements SecretKey + extends org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec { private String algorithm; public RepeatedSecretKeySpec(String algorithm) { - this.algorithm = algorithm; - } - - public String getAlgorithm() - { - return algorithm; - } - - public String getFormat() - { - return null; - } - - public byte[] getEncoded() - { - return null; + super(algorithm); } } diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/package.html b/bcprov/src/main/java/org/bouncycastle/jce/spec/package.html deleted file mode 100644 index 6f37057..0000000 --- a/bcprov/src/main/java/org/bouncycastle/jce/spec/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Parameter specifications for supporting El Gamal, and Elliptic Curve. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java new file mode 100644 index 0000000..69ab797 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java @@ -0,0 +1,20 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/math/ec/DoubleAddMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/DoubleAddMultiplier.java new file mode 100644 index 0000000..aae2e00 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/DoubleAddMultiplier.java @@ -0,0 +1,24 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java index 78a7a8f..d640b5f 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java @@ -7,16 +7,13 @@ public class ECAlgorithms public static ECPoint sumOfTwoMultiplies(ECPoint P, BigInteger a, ECPoint Q, BigInteger b) { - ECCurve c = P.getCurve(); - if (!c.equals(Q.getCurve())) - { - throw new IllegalArgumentException("P and Q must be on same curve"); - } + ECCurve cp = P.getCurve(); + Q = importPoint(cp, Q); // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick - if (c instanceof ECCurve.F2m) + if (cp instanceof ECCurve.F2m) { - ECCurve.F2m f2mCurve = (ECCurve.F2m)c; + ECCurve.F2m f2mCurve = (ECCurve.F2m)cp; if (f2mCurve.isKoblitz()) { return P.multiply(a).add(Q.multiply(b)); @@ -48,43 +45,83 @@ public class ECAlgorithms public static ECPoint shamirsTrick(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) { - if (!P.getCurve().equals(Q.getCurve())) + 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("P and Q must be on same curve"); + throw new IllegalArgumentException("Point must be on the same curve"); } + return c.importPoint(p); + } - return implShamirsTrick(P, k, Q, l); + 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; } - private static ECPoint implShamirsTrick(ECPoint P, BigInteger k, + static ECPoint implShamirsTrick(ECPoint P, BigInteger k, ECPoint Q, BigInteger l) { - int m = Math.max(k.bitLength(), l.bitLength()); - ECPoint Z = P.add(Q); - ECPoint R = P.getCurve().getInfinity(); + 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); - for (int i = m - 1; i >= 0; --i) + 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) { - R = R.twice(); + int jsfi = jsf[i]; + int kDigit = (jsfi >> 4), lDigit = ((jsfi << 28) >> 28); - if (k.testBit(i)) - { - if (l.testBit(i)) - { - R = R.add(Z); - } - else - { - R = R.add(P); - } - } - else - { - if (l.testBit(i)) - { - R = R.add(Q); - } - } + int index = 4 + (kDigit * 3) + lDigit; + R = R.twicePlus(table[index]); } return R; diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java index 58281af..19f0062 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java @@ -3,18 +3,199 @@ package org.bouncycastle.math.ec; import java.math.BigInteger; import java.util.Random; +import org.bouncycastle.util.BigIntegers; + /** * base class for an elliptic curve */ public abstract class ECCurve { - ECFieldElement a, b; + 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 abstract ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression); + 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 <code>PreCompInfo</code> for a point on this curve. Used by + * <code>ECMultiplier</code>s to save the precomputation for this <code>ECPoint</code> for use + * by subsequent multiplication. + * + * @param point + * The <code>ECPoint</code> to store precomputations for. + * @param preCompInfo + * The values precomputed by the <code>ECMultiplier</code>. + */ + 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(); @@ -28,9 +209,26 @@ public abstract class ECCurve return b; } + public int getCoordinateSystem() + { + return coord; + } + protected abstract ECPoint decompressPoint(int yTilde, BigInteger X1); /** + * Sets the default <code>ECMultiplier</code>, 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 * <code>F<sub>p</sub></code> (X9.62 s 4.2.1 pg 17). @@ -62,9 +260,9 @@ public abstract class ECCurve } int yTilde = encoded[0] & 1; - BigInteger X1 = fromArray(encoded, 1, expectedLength); + BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength); - p = decompressPoint(yTilde, X1); + p = decompressPoint(yTilde, X); break; } case 0x04: // uncompressed @@ -76,10 +274,10 @@ public abstract class ECCurve throw new IllegalArgumentException("Incorrect length for uncompressed/hybrid encoding"); } - BigInteger X1 = fromArray(encoded, 1, expectedLength); - BigInteger Y1 = fromArray(encoded, 1 + expectedLength, expectedLength); + BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength); + BigInteger Y = BigIntegers.fromUnsignedByteArray(encoded, 1 + expectedLength, expectedLength); - p = createPoint(X1, Y1, false); + p = createPoint(X, Y); break; } default: @@ -89,11 +287,29 @@ public abstract class ECCurve return p; } - private static BigInteger fromArray(byte[] buf, int off, int length) + protected void checkPoint(ECPoint point) { - byte[] mag = new byte[length]; - System.arraycopy(buf, off, mag, 0, length); - return new BigInteger(1, mag); + 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"); + } + } } /** @@ -101,15 +317,50 @@ public abstract class ECCurve */ public static class Fp extends ECCurve { - BigInteger q; + 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() @@ -124,12 +375,34 @@ public abstract class ECCurve public ECFieldElement fromBigInteger(BigInteger x) { - return new ECFieldElement.Fp(this.q, x); + return new ECFieldElement.Fp(this.q, this.r, x); } - public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression) + protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression) { - return new ECPoint.Fp(this, fromBigInteger(x), fromBigInteger(y), 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) @@ -148,9 +421,7 @@ public abstract class ECCurve } BigInteger betaValue = beta.toBigInteger(); - int bit0 = betaValue.testBit(0) ? 1 : 0; - - if (bit0 != yTilde) + if (betaValue.testBit(0) != (yTilde == 1)) { // Use the other root beta = fromBigInteger(q.subtract(betaValue)); @@ -195,6 +466,8 @@ public abstract class ECCurve */ public static class F2m extends ECCurve { + private static final int F2M_DEFAULT_COORDS = COORD_AFFINE; + /** * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>. */ @@ -401,9 +674,53 @@ public abstract class ECCurve } } + 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() @@ -418,7 +735,32 @@ public abstract class ECCurve public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression) { - return new ECPoint.F2m(this, fromBigInteger(x), fromBigInteger(y), 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() @@ -432,10 +774,7 @@ public abstract class ECCurve */ public boolean isKoblitz() { - return ((n != null) && (h != null) && - ((a.toBigInteger().equals(ECConstants.ZERO)) || - (a.toBigInteger().equals(ECConstants.ONE))) && - (b.toBigInteger().equals(ECConstants.ONE))); + return n != null && h != null && a.bitLength() <= 1 && b.bitLength() == 1; } /** @@ -480,7 +819,7 @@ public abstract class ECCurve { ECFieldElement xp = fromBigInteger(X1); ECFieldElement yp = null; - if (xp.toBigInteger().equals(ECConstants.ZERO)) + if (xp.isZero()) { yp = (ECFieldElement.F2m)b; for (int i = 0; i < m - 1; i++) @@ -491,17 +830,31 @@ public abstract class ECCurve else { ECFieldElement beta = xp.add(a).add(b.multiply(xp.square().invert())); - ECFieldElement z = solveQuadradicEquation(beta); + ECFieldElement z = solveQuadraticEquation(beta); if (z == null) { throw new IllegalArgumentException("Invalid point compression"); } - int zBit = z.toBigInteger().testBit(0) ? 1 : 0; - if (zBit != yTilde) + if (z.testBitZero() != (yTilde == 1)) { - z = z.add(fromBigInteger(ECConstants.ONE)); + 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); @@ -512,28 +865,26 @@ public abstract class ECCurve * D.1.6) The other solution is <code>z + 1</code>. * * @param beta - * The value to solve the qradratic equation for. + * The value to solve the quadratic equation for. * @return the solution for <code>z<sup>2</sup> + z = beta</code> or * <code>null</code> if no solution exists. */ - private ECFieldElement solveQuadradicEquation(ECFieldElement beta) + private ECFieldElement solveQuadraticEquation(ECFieldElement beta) { - ECFieldElement zeroElement = new ECFieldElement.F2m( - this.m, this.k1, this.k2, this.k3, ECConstants.ZERO); - - if (beta.toBigInteger().equals(ECConstants.ZERO)) + if (beta.isZero()) { - return zeroElement; + return beta; } + ECFieldElement zeroElement = fromBigInteger(ECConstants.ZERO); + ECFieldElement z = null; - ECFieldElement gamma = zeroElement; + ECFieldElement gamma = null; Random rand = new Random(); do { - ECFieldElement t = new ECFieldElement.F2m(this.m, this.k1, - this.k2, this.k3, new BigInteger(m, rand)); + ECFieldElement t = fromBigInteger(new BigInteger(m, rand)); z = zeroElement; ECFieldElement w = beta; for (int i = 1; i <= m - 1; i++) @@ -542,13 +893,13 @@ public abstract class ECCurve z = z.square().add(w2.multiply(t)); w = w2.add(beta); } - if (!w.toBigInteger().equals(ECConstants.ZERO)) + if (!w.isZero()) { return null; } gamma = z.square().add(z); } - while (gamma.toBigInteger().equals(ECConstants.ZERO)); + while (gamma.isZero()); return z; } @@ -567,7 +918,7 @@ public abstract class ECCurve } 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); diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java index b5e9aa5..87608eb 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java @@ -3,14 +3,17 @@ package org.bouncycastle.math.ec; import java.math.BigInteger; import java.util.Random; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.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); @@ -19,27 +22,99 @@ public abstract class ECFieldElement 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(2); + return this.toBigInteger().toString(16); + } + + public byte[] getEncoded() + { + return BigIntegers.asUnsignedByteArray((getFieldSize() + 7) / 8, toBigInteger()); } public static class Fp extends ECFieldElement { - BigInteger x; + BigInteger q, r, x; - BigInteger q; - +// 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.x = x; - - if (x.compareTo(q) >= 0) + 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 too large in field element"); + throw new IllegalArgumentException("x value invalid in Fp field element"); } this.q = q; + this.r = r; + this.x = x; } public BigInteger toBigInteger() @@ -66,40 +141,70 @@ public abstract class ECFieldElement { return q; } - + public ECFieldElement add(ECFieldElement b) { - return new Fp(q, x.add(b.toBigInteger()).mod(q)); + 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) { - return new Fp(q, x.subtract(b.toBigInteger()).mod(q)); + 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, x.multiply(b.toBigInteger()).mod(q)); + return new Fp(q, r, modMult(x, b.toBigInteger())); } public ECFieldElement divide(ECFieldElement b) { - return new Fp(q, x.multiply(b.toBigInteger().modInverse(q)).mod(q)); + return new Fp(q, modMult(x, b.toBigInteger().modInverse(q))); } public ECFieldElement negate() { - return new Fp(q, x.negate().mod(q)); + 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, x.multiply(x).mod(q)); + return new Fp(q, r, modMult(x, x)); } public ECFieldElement invert() { - return new Fp(q, x.modInverse(q)); + // TODO Modular inversion can be faster for a (Generalized) Mersenne Prime. + return new Fp(q, r, x.modInverse(q)); } // D.1.4 91 @@ -120,7 +225,7 @@ public abstract class ECFieldElement if (q.testBit(1)) { // z = g^(u+1) + p, p = 4u + 3 - ECFieldElement z = new Fp(q, x.modPow(q.shiftRight(2).add(ECConstants.ONE), q)); + ECFieldElement z = new Fp(q, r, x.modPow(q.shiftRight(2).add(ECConstants.ONE), q)); return z.square().equals(this) ? z : null; } @@ -138,7 +243,7 @@ public abstract class ECFieldElement BigInteger k = u.shiftLeft(1).add(ECConstants.ONE); BigInteger Q = this.x; - BigInteger fourQ = Q.shiftLeft(2).mod(q); + BigInteger fourQ = modDouble(modDouble(Q)); BigInteger U, V; Random rand = new Random(); @@ -152,11 +257,11 @@ public abstract class ECFieldElement while (P.compareTo(q) >= 0 || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent, q).equals(qMinusOne))); - BigInteger[] result = lucasSequence(q, P, Q, k); + BigInteger[] result = lucasSequence(P, Q, k); U = result[0]; V = result[1]; - if (V.multiply(V).mod(q).equals(fourQ)) + if (modMult(V, V).equals(fourQ)) { // Integer division by 2, mod q if (V.testBit(0)) @@ -168,7 +273,7 @@ public abstract class ECFieldElement //assert V.multiply(V).mod(q).equals(x); - return new ECFieldElement.Fp(q, V); + return new ECFieldElement.Fp(q, r, V); } } while (U.equals(ECConstants.ONE) || U.equals(qMinusOne)); @@ -230,8 +335,7 @@ public abstract class ECFieldElement // return r.multiply(r).multiply(x.modPow(q.subtract(ECConstants.TWO), q)).subtract(ECConstants.TWO).mod(p); // } - private static BigInteger[] lucasSequence( - BigInteger p, + private BigInteger[] lucasSequence( BigInteger P, BigInteger Q, BigInteger k) @@ -247,40 +351,122 @@ public abstract class ECFieldElement for (int j = n - 1; j >= s + 1; --j) { - Ql = Ql.multiply(Qh).mod(p); + Ql = modMult(Ql, Qh); if (k.testBit(j)) { - Qh = Ql.multiply(Q).mod(p); - Uh = Uh.multiply(Vh).mod(p); - Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p); - Vh = Vh.multiply(Vh).subtract(Qh.shiftLeft(1)).mod(p); + 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 = Uh.multiply(Vl).subtract(Ql).mod(p); - Vh = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p); - Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p); + 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 = Ql.multiply(Qh).mod(p); - Qh = Ql.multiply(Q).mod(p); - Uh = Uh.multiply(Vl).subtract(Ql).mod(p); - Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p); - Ql = Ql.multiply(Qh).mod(p); + 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 = Uh.multiply(Vl).mod(p); - Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p); - Ql = Ql.multiply(Ql).mod(p); + 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) @@ -669,7 +855,7 @@ public abstract class ECFieldElement // g1z = g1z.xor(g2z.shiftLeft(j)); //// if (g1z.bitLength() > this.m) { //// throw new ArithmeticException( -//// "deg(g1z) >= m, g1z = " + g1z.toString(2)); +//// "deg(g1z) >= m, g1z = " + g1z.toString(16)); //// } // } // return new ECFieldElement.F2m( @@ -801,41 +987,38 @@ public abstract class ECFieldElement */ private int m; - /** - * TPB: The integer <code>k</code> where <code>x<sup>m</sup> + - * x<sup>k</sup> + 1</code> represents the reduction polynomial - * <code>f(z)</code>.<br> - * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> + - * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> - * represents the reduction polynomial <code>f(z)</code>.<br> - */ - private int k1; - - /** - * TPB: Always set to <code>0</code><br> - * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> + - * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> - * represents the reduction polynomial <code>f(z)</code>.<br> - */ - private int k2; - - /** - * TPB: Always set to <code>0</code><br> - * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> + - * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> - * represents the reduction polynomial <code>f(z)</code>.<br> - */ - private int k3; +// /** +// * TPB: The integer <code>k</code> where <code>x<sup>m</sup> + +// * x<sup>k</sup> + 1</code> represents the reduction polynomial +// * <code>f(z)</code>.<br> +// * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> + +// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> +// * represents the reduction polynomial <code>f(z)</code>.<br> +// */ +// private int k1; +// +// /** +// * TPB: Always set to <code>0</code><br> +// * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> + +// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> +// * represents the reduction polynomial <code>f(z)</code>.<br> +// */ +// private int k2; +// +// /** +// * TPB: Always set to <code>0</code><br> +// * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> + +// * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> +// * represents the reduction polynomial <code>f(z)</code>.<br> +// */ +// private int k3; - /** - * The <code>IntArray</code> holding the bits. - */ - private IntArray x; + private int[] ks; /** - * The number of <code>int</code>s required to hold <code>m</code> bits. + * The <code>LongArray</code> holding the bits. */ - private int t; + private LongArray x; /** * Constructor for PPB. @@ -851,6 +1034,7 @@ public abstract class ECFieldElement * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code> * represents the reduction polynomial <code>f(z)</code>. * @param x The BigInteger representing the value of the field element. + * @deprecated Use ECCurve.fromBigInteger to construct field elements */ public F2m( int m, @@ -859,13 +1043,10 @@ public abstract class ECFieldElement int k3, BigInteger x) { - // t = m / 32 rounded up to the next integer - t = (m + 31) >> 5; - this.x = new IntArray(x, t); - if ((k2 == 0) && (k3 == 0)) { this.representation = TPB; + this.ks = new int[]{ k1 }; } else { @@ -880,17 +1061,11 @@ public abstract class ECFieldElement "k2 must be larger than 0"); } this.representation = PPB; - } - - if (x.signum() < 0) - { - throw new IllegalArgumentException("x value cannot be negative"); + this.ks = new int[]{ k1, k2, k3 }; } this.m = m; - this.k1 = k1; - this.k2 = k2; - this.k3 = k3; + this.x = new LongArray(x); } /** @@ -901,6 +1076,7 @@ public abstract class ECFieldElement * x<sup>k</sup> + 1</code> represents the reduction * polynomial <code>f(z)</code>. * @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) { @@ -908,24 +1084,27 @@ public abstract class ECFieldElement this(m, k, 0, 0, x); } - private F2m(int m, int k1, int k2, int k3, IntArray x) + private F2m(int m, int[] ks, LongArray x) { - t = (m + 31) >> 5; - this.x = x; this.m = m; - this.k1 = k1; - this.k2 = k2; - this.k3 = k3; + this.representation = (ks.length == 1) ? TPB : PPB; + this.ks = ks; + this.x = x; + } - if ((k2 == 0) && (k3 == 0)) - { - this.representation = TPB; - } - else - { - this.representation = PPB; - } + public int bitLength() + { + return x.degree(); + } + + public boolean isZero() + { + return x.isZero(); + } + public boolean testBitZero() + { + return x.testBitZero(); } public BigInteger toBigInteger() @@ -967,19 +1146,15 @@ public abstract class ECFieldElement 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)) + if (aF2m.representation != bF2m.representation) { - throw new IllegalArgumentException("Field elements are not " - + "elements of the same field F2m"); + // Should never occur + throw new IllegalArgumentException("One of the F2m field elements has incorrect representation"); } - if (aF2m.representation != bF2m.representation) + if ((aF2m.m != bF2m.m) || !Arrays.areEqual(aF2m.ks, bF2m.ks)) { - // Should never occur - throw new IllegalArgumentException( - "One of the field " - + "elements are not elements has incorrect representation"); + throw new IllegalArgumentException("Field elements are not elements of the same field F2m"); } } @@ -988,10 +1163,15 @@ public abstract class ECFieldElement // No check performed here for performance reasons. Instead the // elements involved are checked in ECPoint.F2m // checkFieldElements(this, b); - IntArray iarrClone = (IntArray)this.x.clone(); + LongArray iarrClone = (LongArray)this.x.clone(); F2m bF2m = (F2m)b; - iarrClone.addShifted(bF2m.x, 0); - return new F2m(m, k1, k2, k3, iarrClone); + 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) @@ -1002,17 +1182,14 @@ public abstract class ECFieldElement public ECFieldElement multiply(final ECFieldElement b) { - // Right-to-left comb multiplication in the IntArray + // 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); - F2m bF2m = (F2m)b; - IntArray mult = x.multiply(bF2m.x, m); - mult.reduce(m, new int[]{k1, k2, k3}); - return new F2m(m, k1, k2, k3, mult); + return new F2m(m, ks, x.modMultiply(((F2m)b).x, m, ks)); } public ECFieldElement divide(final ECFieldElement b) @@ -1030,80 +1207,12 @@ public abstract class ECFieldElement public ECFieldElement square() { - IntArray squared = x.square(m); - squared.reduce(m, new int[]{k1, k2, k3}); - return new F2m(m, k1, k2, k3, squared); + return new F2m(m, ks, x.modSquare(m, ks)); } - 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) - IntArray uz = (IntArray)this.x.clone(); - - // v(z) := f(z) - IntArray vz = new IntArray(t); - vz.setBit(m); - vz.setBit(0); - vz.setBit(this.k1); - if (this.representation == PPB) - { - vz.setBit(this.k2); - vz.setBit(this.k3); - } - - // g1(z) := 1, g2(z) := 0 - IntArray g1z = new IntArray(t); - g1z.setBit(0); - IntArray g2z = new IntArray(t); - - // while u != 0 - while (!uz.isZero()) -// while (uz.getUsedLength() > 0) -// while (uz.bitLength() > 1) - { - // 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 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)); - // jInt = n / 32 - int jInt = j >> 5; - // jInt = n % 32 - int jBit = j & 0x1F; - IntArray vzShift = vz.shiftLeft(jBit); - uz.addShifted(vzShift, jInt); - - // g1(z) := g1(z) + z^j * g2(z) -// g1z = g1z.xor(g2z.shiftLeft(j)); - IntArray g2zShift = g2z.shiftLeft(jBit); - g1z.addShifted(g2zShift, jInt); - - } - return new ECFieldElement.F2m( - this.m, this.k1, this.k2, this.k3, g2z); + return new ECFieldElement.F2m(this.m, this.ks, this.x.modInverse(m, ks)); } public ECFieldElement sqrt() @@ -1143,7 +1252,7 @@ public abstract class ECFieldElement */ public int getK1() { - return this.k1; + return this.ks[0]; } /** @@ -1154,7 +1263,7 @@ public abstract class ECFieldElement */ public int getK2() { - return this.k2; + return this.ks.length >= 2 ? this.ks[1] : 0; } /** @@ -1165,7 +1274,7 @@ public abstract class ECFieldElement */ public int getK3() { - return this.k3; + return this.ks.length >= 3 ? this.ks[2] : 0; } public boolean equals(Object anObject) @@ -1182,15 +1291,15 @@ public abstract class ECFieldElement ECFieldElement.F2m b = (ECFieldElement.F2m)anObject; - return ((this.m == b.m) && (this.k1 == b.k1) && (this.k2 == b.k2) - && (this.k3 == b.k3) + return ((this.m == b.m) && (this.representation == b.representation) + && Arrays.areEqual(this.ks, b.ks) && (this.x.equals(b.x))); } public int hashCode() { - return x.hashCode() ^ m ^ k1 ^ k2 ^ k3; + return x.hashCode() ^ m ^ Arrays.hashCode(ks); } } } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java index 4d72e33..da1013a 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java @@ -6,14 +6,14 @@ import java.math.BigInteger; * Interface for classes encapsulating a point multiplication algorithm * for <code>ECPoint</code>s. */ -interface ECMultiplier +public interface ECMultiplier { /** * Multiplies the <code>ECPoint p</code> by <code>k</code>, i.e. * <code>p</code> is added <code>k</code> times to itself. * @param p The <code>ECPoint</code> to be multiplied. - * @param k The factor by which <code>p</code> i multiplied. + * @param k The factor by which <code>p</code> is multiplied. * @return <code>p</code> multiplied by <code>k</code>. */ - ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo); + ECPoint multiply(ECPoint p, BigInteger k); } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java index cbc5aaf..7f740e4 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java @@ -2,59 +2,324 @@ package org.bouncycastle.math.ec; import java.math.BigInteger; -import org.bouncycastle.asn1.x9.X9IntegerConverter; - /** * base class for points on elliptic curves. */ public abstract class ECPoint { - ECCurve curve; - ECFieldElement x; - ECFieldElement y; + protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0]; - protected boolean withCompression; + protected static ECFieldElement[] getInitialZCoords(ECCurve curve) + { + // Cope with null curve, most commonly used by implicitlyCa + int coord = null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem(); - protected ECMultiplier multiplier = null; + switch (coord) + { + case ECCurve.COORD_AFFINE: + case ECCurve.COORD_LAMBDA_AFFINE: + return EMPTY_ZS; + default: + break; + } - protected PreCompInfo preCompInfo = null; + ECFieldElement one = curve.fromBigInteger(ECConstants.ONE); - private static X9IntegerConverter converter = new X9IntegerConverter(); + 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 x; + 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; + return x == null || y == null || (zs.length > 0 && zs[0].isZero()); } public boolean isCompressed() { - return withCompression; + 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) + public boolean equals(Object other) { if (other == this) { @@ -66,69 +331,117 @@ public abstract class ECPoint return false; } - ECPoint o = (ECPoint)other; + return equals((ECPoint)other); + } - if (this.isInfinity()) + public int hashCode() + { + ECCurve c = this.getCurve(); + int hc = (null == c) ? 0 : ~c.hashCode(); + + if (!this.isInfinity()) { - return o.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 x.equals(o.x) && y.equals(o.y); + return hc; } - public int hashCode() + public String toString() { if (this.isInfinity()) { - return 0; + return "INF"; } - - return x.hashCode() ^ y.hashCode(); + + 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(); } -// /** -// * Mainly for testing. Explicitly set the <code>ECMultiplier</code>. -// * @param multiplier The <code>ECMultiplier</code> to be used to multiply -// * this <code>ECPoint</code>. -// */ -// public void setECMultiplier(ECMultiplier multiplier) -// { -// this.multiplier = multiplier; -// } + public byte[] getEncoded() + { + return getEncoded(this.withCompression); + } /** - * Sets the <code>PreCompInfo</code>. Used by <code>ECMultiplier</code>s - * to save the precomputation for this <code>ECPoint</code> to store the - * precomputation result for use by subsequent multiplication. - * @param preCompInfo The values precomputed by the - * <code>ECMultiplier</code>. + * return the field element encoded with point compression. (S 4.3.6) */ - void setPreCompInfo(PreCompInfo preCompInfo) + public byte[] getEncoded(boolean compressed) { - this.preCompInfo = preCompInfo; - } + if (this.isInfinity()) + { + return new byte[1]; + } - public byte[] getEncoded() - { - return getEncoded(withCompression); + 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; } - public abstract byte[] getEncoded(boolean compressed); + protected abstract boolean getCompressionYTilde(); public abstract ECPoint add(ECPoint b); - public abstract ECPoint subtract(ECPoint b); + public abstract ECPoint negate(); - public abstract ECPoint twice(); - /** - * Sets the default <code>ECMultiplier</code>, unless already set. - */ - synchronized void assertECMultiplier() + public abstract ECPoint subtract(ECPoint b); + + public ECPoint timesPow2(int e) { - if (this.multiplier == null) + if (e < 0) + { + throw new IllegalArgumentException("'e' cannot be negative"); + } + + ECPoint p = this; + while (--e >= 0) { - this.multiplier = new FpNafMultiplier(); + p = p.twice(); } + return p; + } + + public abstract ECPoint twice(); + + public ECPoint twicePlus(ECPoint b) + { + return twice().add(b); + } + + public ECPoint threeTimes() + { + return twicePlus(this); } /** @@ -138,23 +451,7 @@ public abstract class ECPoint */ public ECPoint multiply(BigInteger k) { - if (k.signum() < 0) - { - throw new IllegalArgumentException("The multiplicator cannot be negative"); - } - - if (this.isInfinity()) - { - return this; - } - - if (k.signum() == 0) - { - return this.curve.getInfinity(); - } - - assertECMultiplier(); - return this.multiplier.multiply(this, k, preCompInfo); + return this.getCurve().getMultiplier().multiply(this, k); } /** @@ -162,13 +459,14 @@ public abstract class ECPoint */ 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) { @@ -182,6 +480,8 @@ public abstract class ECPoint * @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) { @@ -194,112 +494,537 @@ public abstract class ECPoint this.withCompression = withCompression; } - - /** - * return the field element encoded with point compression. (S 4.3.6) - */ - public byte[] getEncoded(boolean compressed) + + Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) { - if (this.isInfinity()) + 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 new byte[1]; + return getJacobianModifiedW(); } - int qLength = converter.getByteLength(x); - - if (compressed) + return super.getZCoord(index); + } + + // B.3 pg 62 + public ECPoint add(ECPoint b) + { + if (this.isInfinity()) { - byte PC; - - if (this.getY().toBigInteger().testBit(0)) + 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()) { - PC = 0x03; + 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(); } - else + + 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()) { - PC = 0x02; + 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(); + } - byte[] X = converter.integerToBytes(this.getX().toBigInteger(), qLength); - byte[] PO = new byte[X.length + 1]; + ECFieldElement HSquared = H.square(); + ECFieldElement G = HSquared.multiply(H); + ECFieldElement V = HSquared.multiply(U1); - PO[0] = PC; - System.arraycopy(X, 0, PO, 1, X.length); + X3 = R.square().add(G).subtract(two(V)); + Y3 = V.subtract(X3).multiply(R).subtract(S1.multiply(G)); - return PO; + 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); } - else + 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: { - byte[] X = converter.integerToBytes(this.getX().toBigInteger(), qLength); - byte[] Y = converter.integerToBytes(this.getY().toBigInteger(), qLength); - byte[] PO = new byte[X.length + Y.length + 1]; + 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())); - PO[0] = 0x04; - System.arraycopy(X, 0, PO, 1, X.length); - System.arraycopy(Y, 0, PO, X.length + 1, Y.length); + 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); + } - return PO; + // 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"); + } } } - // B.3 pg 62 - public ECPoint add(ECPoint b) + public ECPoint twicePlus(ECPoint b) { + if (this == b) + { + return threeTimes(); + } if (this.isInfinity()) { return b; } - if (b.isInfinity()) { - return this; + return twice(); + } + + ECFieldElement Y1 = this.y; + if (Y1.isZero()) + { + return b; } - // Check if b = this or b = -this - if (this.x.equals(b.x)) + ECCurve curve = this.getCurve(); + int coord = curve.getCoordinateSystem(); + + switch (coord) + { + case ECCurve.COORD_AFFINE: { - if (this.y.equals(b.y)) + ECFieldElement X1 = this.x; + ECFieldElement X2 = b.x, Y2 = b.y; + + ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1); + + if (dx.isZero()) { - // this = b, i.e. this must be doubled - return this.twice(); + if (dy.isZero()) + { + // this == b i.e. the result is 3P + return threeTimes(); + } + + // this == -b, i.e. the result is P + return this; } - // this = -b, i.e. the result is the point at infinity - return this.curve.getInfinity(); - } + /* + * Optimized calculation of 2P + Q, as described in "Trading Inversions for + * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery. + */ - ECFieldElement gamma = b.y.subtract(this.y).divide(b.x.subtract(this.x)); + ECFieldElement X = dx.square(), Y = dy.square(); + ECFieldElement d = X.multiply(two(X1).add(X2)).subtract(Y); + if (d.isZero()) + { + return curve.getInfinity(); + } - ECFieldElement x3 = gamma.square().subtract(this.x).subtract(b.x); - ECFieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); + 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, x3, y3, withCompression); + return new ECPoint.Fp(curve, X4, Y4, this.withCompression); + } + case ECCurve.COORD_JACOBIAN_MODIFIED: + { + return twiceJacobianModified(false).add(b); + } + default: + { + return twice().add(b); + } + } } - // B.3 pg 62 - public ECPoint twice() + public ECPoint threeTimes() { - if (this.isInfinity()) + if (this.isInfinity() || this.y.isZero()) { - // Twice identity element (point at infinity) is identity return this; } - if (this.y.toBigInteger().signum() == 0) + ECCurve curve = this.getCurve(); + int coord = curve.getCoordinateSystem(); + + switch (coord) + { + case ECCurve.COORD_AFFINE: { - // if y1 == 0, then (x1, y1) == (x1, -y1) - // and hence this = -this and thus 2(x1, y1) == infinity - return this.curve.getInfinity(); + 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); } + } + } - ECFieldElement TWO = this.curve.fromBigInteger(BigInteger.valueOf(2)); - ECFieldElement THREE = this.curve.fromBigInteger(BigInteger.valueOf(3)); - ECFieldElement gamma = this.x.square().multiply(THREE).add(curve.a).divide(y.multiply(TWO)); + protected ECFieldElement two(ECFieldElement x) + { + return x.add(x); + } - ECFieldElement x3 = gamma.square().subtract(this.x.multiply(TWO)); - ECFieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y); - - return new ECPoint.Fp(curve, x3, y3, this.withCompression); + 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:) @@ -316,18 +1041,70 @@ public abstract class ECPoint 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); } - /** - * Sets the default <code>ECMultiplier</code>, unless already set. - */ - synchronized void assertECMultiplier() + protected ECFieldElement calculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared) { - if (this.multiplier == null) + if (ZSquared == null) + { + ZSquared = Z.square(); + } + + ECFieldElement W = ZSquared.square(); + ECFieldElement a4 = this.getCurve().getA(); + ECFieldElement a4Neg = a4.negate(); + if (a4Neg.bitLength() < a4.bitLength()) { - this.multiplier = new WNafMultiplier(); + 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); } } @@ -340,6 +1117,8 @@ public abstract class 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) { @@ -351,6 +1130,8 @@ public abstract class ECPoint * @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) { @@ -360,71 +1141,91 @@ public abstract class ECPoint { 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(); } - /* (non-Javadoc) - * @see org.bouncycastle.math.ec.ECPoint#getEncoded() - */ - public byte[] getEncoded(boolean compressed) + F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression) { - if (this.isInfinity()) - { - return new byte[1]; - } + super(curve, x, y, zs); + + this.withCompression = withCompression; + +// checkCurveEquation(); + } - int byteCount = converter.getByteLength(this.x); - byte[] X = converter.integerToBytes(this.getX().toBigInteger(), byteCount); - byte[] PO; + public ECFieldElement getYCoord() + { + int coord = this.getCurveCoordinateSystem(); - if (compressed) + switch (coord) + { + case ECCurve.COORD_LAMBDA_AFFINE: + case ECCurve.COORD_LAMBDA_PROJECTIVE: { - // See X9.62 4.3.6 and 4.2.2 - PO = new byte[byteCount + 1]; + // TODO The X == 0 stuff needs further thought + if (this.isInfinity() || x.isZero()) + { + return y; + } - PO[0] = 0x02; - // X9.62 4.2.2 and 4.3.6: - // if x = 0 then ypTilde := 0, else ypTilde is the rightmost - // bit of y * x^(-1) - // if ypTilde = 0, then PC := 02, else PC := 03 - // Note: PC === PO[0] - if (!(this.getX().toBigInteger().equals(ECConstants.ZERO))) + // 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) { - if (this.getY().multiply(this.getX().invert()) - .toBigInteger().testBit(0)) + ECFieldElement Z = zs[0]; + if (Z.bitLength() != 1) { - // ypTilde = 1, hence PC = 03 - PO[0] = 0x03; + Y = Y.divide(Z); } } - - System.arraycopy(X, 0, PO, 1, byteCount); + return Y; } - else + default: { - byte[] Y = converter.integerToBytes(this.getY().toBigInteger(), byteCount); - - PO = new byte[byteCount + byteCount + 1]; - - PO[0] = 0x04; - System.arraycopy(X, 0, PO, 1, byteCount); - System.arraycopy(Y, 0, PO, byteCount + 1, byteCount); + return y; + } + } + } + + protected boolean getCompressionYTilde() + { + ECFieldElement X = this.getRawXCoord(); + if (X.isZero()) + { + return false; } - return PO; + 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(); + } + } } /** @@ -437,7 +1238,7 @@ public abstract class ECPoint private static void checkPoints(ECPoint a, ECPoint b) { // Check, if points are on the same curve - if (!(a.curve.equals(b.curve))) + if (a.curve != b.curve) { throw new IllegalArgumentException("Only points on the same " + "curve can be added or subtracted"); @@ -466,43 +1267,162 @@ public abstract class ECPoint */ public ECPoint.F2m addSimple(ECPoint.F2m b) { - ECPoint.F2m other = b; if (this.isInfinity()) { - return other; + return b; } - - if (other.isInfinity()) + if (b.isInfinity()) { return this; } - ECFieldElement.F2m x2 = (ECFieldElement.F2m)other.getX(); - ECFieldElement.F2m y2 = (ECFieldElement.F2m)other.getY(); + 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); - // Check if other = this or other = -this - if (this.x.equals(x2)) + return new ECPoint.F2m(curve, X3, Y3, this.withCompression); + } + case ECCurve.COORD_HOMOGENEOUS: { - if (this.y.equals(y2)) + 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)) { - // this = other, i.e. this must be doubled - return (ECPoint.F2m)this.twice(); + if (U1.equals(U2)) + { + return (ECPoint.F2m)twice(); + } + + return (ECPoint.F2m)curve.getInfinity(); } - // this = -other, i.e. the result is the point at infinity - return (ECPoint.F2m)this.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.F2m lambda - = (ECFieldElement.F2m)(this.y.add(y2)).divide(this.x.add(x2)); + 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); + } - ECFieldElement.F2m x3 - = (ECFieldElement.F2m)lambda.square().add(lambda).add(this.x).add(x2).add(this.curve.getA()); + boolean Z2IsOne = Z2.bitLength() == 1; + ECFieldElement U1 = X1, S1 = L1; + if (!Z2IsOne) + { + U1 = U1.multiply(Z2); + S1 = S1.multiply(Z2); + } - ECFieldElement.F2m y3 - = (ECFieldElement.F2m)lambda.multiply(this.x.add(x3)).add(x3).add(this.y); + ECFieldElement A = S1.add(S2); + ECFieldElement B = U1.add(U2); - return new ECPoint.F2m(curve, x3, y3, withCompression); + 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) @@ -534,59 +1454,279 @@ public abstract class ECPoint return addSimple((ECPoint.F2m)b.negate()); } - /* (non-Javadoc) - * @see org.bouncycastle.math.ec.ECPoint#twice() - */ + 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()) { - // Twice identity element (point at infinity) is identity return this; } - if (this.x.toBigInteger().signum() == 0) + ECCurve curve = this.getCurve(); + + ECFieldElement X1 = this.x; + if (X1.isZero()) { - // if x1 == 0, then (x1, y1) == (x1, x1 + y1) - // and hence this = -this and thus 2(x1, y1) == infinity - return this.curve.getInfinity(); + // A point with X == 0 is it's own additive inverse + return curve.getInfinity(); } - ECFieldElement.F2m lambda - = (ECFieldElement.F2m)this.x.add(this.y.divide(this.x)); + int coord = curve.getCoordinateSystem(); - ECFieldElement.F2m x3 - = (ECFieldElement.F2m)lambda.square().add(lambda). - add(this.curve.getA()); + switch (coord) + { + case ECCurve.COORD_AFFINE: + { + ECFieldElement Y1 = this.y; - ECFieldElement ONE = this.curve.fromBigInteger(ECConstants.ONE); - ECFieldElement.F2m y3 - = (ECFieldElement.F2m)this.x.square().add( - x3.multiply(lambda.add(ONE))); + ECFieldElement L1 = Y1.divide(X1).add(X1); - return new ECPoint.F2m(this.curve, x3, y3, withCompression); - } + ECFieldElement X3 = L1.square().add(L1).add(curve.getA()); + ECFieldElement Y3 = X1.square().add(X3.multiply(L1.addOne())); - public ECPoint negate() - { - return new ECPoint.F2m(curve, this.getX(), this.getY().add(this.getX()), withCompression); + 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"); + } + } } - /** - * Sets the appropriate <code>ECMultiplier</code>, unless already set. - */ - synchronized void assertECMultiplier() + public ECPoint twicePlus(ECPoint b) { - if (this.multiplier == null) + if (this.isInfinity()) + { + return b; + } + if (b.isInfinity()) { - if (((ECCurve.F2m)this.curve).isKoblitz()) + 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) { - this.multiplier = new WTauNafMultiplier(); + return twice().add(b); } - else + + 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))) { - this.multiplier = new WNafMultiplier(); + 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/bcprov/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java deleted file mode 100644 index 35e601d..0000000 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.bouncycastle.math.ec; - -import java.math.BigInteger; - -/** - * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm. - */ -class FpNafMultiplier implements ECMultiplier -{ - /** - * D.3.2 pg 101 - * @see org.bouncycastle.math.ec.ECMultiplier#multiply(org.bouncycastle.math.ec.ECPoint, java.math.BigInteger) - */ - public ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo) - { - // TODO Probably should try to add this - // BigInteger e = k.mod(n); // n == order of p - BigInteger e = k; - BigInteger h = e.multiply(BigInteger.valueOf(3)); - - ECPoint neg = p.negate(); - ECPoint R = p; - - for (int i = h.bitLength() - 2; i > 0; --i) - { - R = R.twice(); - - boolean hBit = h.testBit(i); - boolean eBit = e.testBit(i); - - if (hBit != eBit) - { - R = R.add(hBit ? p : neg); - } - } - - return R; - } -} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java b/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java index ead38c4..34395a5 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java @@ -6,6 +6,60 @@ 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; @@ -22,16 +76,12 @@ class IntArray public IntArray(BigInteger bigInt) { - this(bigInt, 0); - } - - public IntArray(BigInteger bigInt, int minIntLen) - { - if (bigInt.signum() == -1) + if (bigInt == null || bigInt.signum() < 0) { - throw new IllegalArgumentException("Only positive Integers allowed"); + throw new IllegalArgumentException("invalid F2m field value"); } - if (bigInt.equals(ECConstants.ZERO)) + + if (bigInt.signum() == 0) { m_ints = new int[] { 0 }; return; @@ -48,14 +98,7 @@ class IntArray barrStart = 1; } int intLen = (barrLen + 3) / 4; - if (intLen < minIntLen) - { - m_ints = new int[minIntLen]; - } - else - { - m_ints = new int[intLen]; - } + m_ints = new int[intLen]; int iarrJ = intLen - 1; int rem = barrLen % 4 + barrStart; @@ -66,11 +109,7 @@ class IntArray for (; barrI < rem; barrI++) { temp <<= 8; - int barrBarrI = barr[barrI]; - if (barrBarrI < 0) - { - barrBarrI += 256; - } + int barrBarrI = barr[barrI] & 0xFF; temp |= barrBarrI; } m_ints[iarrJ--] = temp; @@ -82,11 +121,7 @@ class IntArray for (int i = 0; i < 4; i++) { temp <<= 8; - int barrBarrI = barr[barrI++]; - if (barrBarrI < 0) - { - barrBarrI += 256; - } + int barrBarrI = barr[barrI++] & 0xFF; temp |= barrBarrI; } m_ints[iarrJ] = temp; @@ -95,88 +130,86 @@ class IntArray public boolean isZero() { - return m_ints.length == 0 - || (m_ints[0] == 0 && getUsedLength() == 0); + int[] a = m_ints; + for (int i = 0; i < a.length; ++i) + { + if (a[i] != 0) + { + return false; + } + } + return true; } public int getUsedLength() { - int highestIntPos = m_ints.length; + return getUsedLengthFrom(m_ints.length); + } - if (highestIntPos < 1) + 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 (m_ints[0] != 0) + if (a[0] != 0) { - while (m_ints[--highestIntPos] == 0) + while (a[--from] == 0) { } - return highestIntPos + 1; + return from + 1; } do { - if (m_ints[--highestIntPos] != 0) + if (a[--from] != 0) { - return highestIntPos + 1; + return from + 1; } } - while (highestIntPos > 0); + while (from > 0); return 0; } - public int bitLength() + public int degree() { - // JDK 1.5: see Integer.numberOfLeadingZeros() - int intLen = getUsedLength(); - if (intLen == 0) - { - return 0; - } - - int last = intLen - 1; - int highest = m_ints[last]; - int bits = (last << 5) + 1; - - // A couple of binary search steps - if ((highest & 0xffff0000) != 0) + int i = m_ints.length, w; + do { - if ((highest & 0xff000000) != 0) - { - bits += 24; - highest >>>= 24; - } - else + if (i == 0) { - bits += 16; - highest >>>= 16; + return 0; } + w = m_ints[--i]; } - else if (highest > 0x000000ff) - { - bits += 8; - highest >>>= 8; - } + while (w == 0); - while (highest != 1) + return (i << 5) + bitLength(w); + } + + private static int bitLength(int w) + { + int t = w >>> 16; + if (t == 0) { - ++bits; - highest >>>= 1; + t = w >>> 8; + return (t == 0) ? bitLengths[w] : 8 + bitLengths[t]; } - return bits; + int u = t >>> 8; + return (u == 0) ? 16 + bitLengths[t] : 24 + bitLengths[u]; } private int[] resizedInts(int newLen) { int[] newInts = new int[newLen]; - int oldLen = m_ints.length; - int copyLen = oldLen < newLen ? oldLen : newLen; - System.arraycopy(m_ints, 0, newInts, 0, copyLen); + System.arraycopy(m_ints, 0, newInts, 0, Math.min(m_ints.length, newLen)); return newInts; } @@ -220,86 +253,128 @@ class IntArray return new BigInteger(1, barr); } - public void shiftLeft() + private static int shiftLeft(int[] x, int count) { - int usedLen = getUsedLength(); - if (usedLen == 0) + 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; } - if (m_ints[usedLen - 1] < 0) + + int minLen = otherUsedLen + words + 1; + if (minLen > m_ints.length) { - // highest bit of highest used byte is set, so shifting left will - // make the IntArray one byte longer - usedLen++; - if (usedLen > m_ints.length) - { - // make the m_ints one byte longer, because we need one more - // byte which is not available in m_ints - m_ints = resizedInts(m_ints.length + 1); - } + m_ints = resizedInts(minLen); } - boolean carry = false; - for (int i = 0; i < usedLen; i++) + int shiftInv = 32 - shift, prev = 0; + for (int i = 0; i < otherUsedLen; ++i) { - // nextCarry is true if highest bit is set - boolean nextCarry = m_ints[i] < 0; - m_ints[i] <<= 1; - if (carry) - { - // set lowest bit - m_ints[i] |= 1; - } - carry = nextCarry; + int next = other.m_ints[i]; + m_ints[i + words] ^= (next << shift) | prev; + prev = next >>> shiftInv; } + m_ints[otherUsedLen + words] ^= prev; } - public IntArray shiftLeft(int n) + private static int addShiftedByBits(int[] x, int[] y, int count, int shift) { - int usedLen = getUsedLength(); - if (usedLen == 0) + int shiftInv = 32 - shift, prev = 0; + for (int i = 0; i < count; ++i) { - return this; + int next = y[i]; + x[i] ^= (next << shift) | prev; + prev = next >>> shiftInv; } + return prev; + } - if (n == 0) + 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) { - return this; + int next = y[yOff + i]; + x[xOff + i] ^= (next << shift) | prev; + prev = next >>> shiftInv; } + return prev; + } - if (n > 31) + public void addShiftedByWords(IntArray other, int words) + { + int otherUsedLen = other.getUsedLength(); + if (otherUsedLen == 0) { - throw new IllegalArgumentException("shiftLeft() for max 31 bits " - + ", " + n + "bit shift is not possible"); + return; } - int[] newInts = new int[usedLen + 1]; + int minLen = otherUsedLen + words; + if (minLen > m_ints.length) + { + m_ints = resizedInts(minLen); + } - int nm32 = 32 - n; - newInts[0] = m_ints[0] << n; - for (int i = 1; i < usedLen; i++) + for (int i = 0; i < otherUsedLen; i++) { - newInts[i] = (m_ints[i] << n) | (m_ints[i - 1] >>> nm32); + m_ints[words + i] ^= other.m_ints[i]; } - newInts[usedLen] = m_ints[usedLen - 1] >>> nm32; + } - return new IntArray(newInts); + private static void addShiftedByWords(int[] x, int xOff, int[] y, int count) + { + for (int i = 0; i < count; ++i) + { + x[xOff + i] ^= y[i]; + } } - public void addShifted(IntArray other, int shift) + private static void add(int[] x, int[] y, int count) { - int usedLenOther = other.getUsedLength(); - int newMinUsedLen = usedLenOther + shift; - if (newMinUsedLen > m_ints.length) + for (int i = 0; i < count; ++i) { - m_ints = resizedInts(newMinUsedLen); - //System.out.println("Resize required"); + x[i] ^= y[i]; } + } - for (int i = 0; i < usedLenOther; i++) + private static void distribute(int[] x, int dst1, int dst2, int src, int count) + { + for (int i = 0; i < count; ++i) { - m_ints[i + shift] ^= other.m_ints[i]; + int v = x[src + i]; + x[dst1 + i] ^= v; + x[dst2 + i] ^= v; } } @@ -308,10 +383,58 @@ class IntArray 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; + int theInt = n >>> 5; // theBit = n % 32 int theBit = n & 0x1F; int tester = 1 << theBit; @@ -321,7 +444,7 @@ class IntArray public void flipBit(int n) { // theInt = n / 32 - int theInt = n >> 5; + int theInt = n >>> 5; // theBit = n % 32 int theBit = n & 0x1F; int flipper = 1 << theBit; @@ -331,127 +454,344 @@ class IntArray public void setBit(int n) { // theInt = n / 32 - int theInt = n >> 5; + 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) { - // Lenght of c is 2m bits rounded up to the next int (32 bit) - int t = (m + 31) >> 5; - if (m_ints.length < t) + 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) { - m_ints = resizedInts(t); + ++bExt; } - IntArray b = new IntArray(other.resizedInts(other.getLength() + 1)); - IntArray c = new IntArray((m + m + 31) >> 5); - // IntArray c = new IntArray(t + t); - int testBit = 1; - for (int k = 0; k < 32; k++) + 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 j = 0; j < t; j++) + for (int aPos = 0; aPos < aLen; ++aPos) { - if ((m_ints[j] & testBit) != 0) + int index = (c[bExt + aPos] >>> k) & MASK; + if (index != 0) { - // The kth bit of m_ints[j] is set - c.addShifted(b, j); + addShiftedByWords(c, aPos + ci[index], c, bExt); } } - testBit <<= 1; - b.shiftLeft(); - } - return c; - } - - // public IntArray multiplyLeftToRight(IntArray other, int m) { - // // Lenght of c is 2m bits rounded up to the next int (32 bit) - // int t = (m + 31) / 32; - // if (m_ints.length < t) { - // m_ints = resizedInts(t); - // } - // - // IntArray b = new IntArray(other.resizedInts(other.getLength() + 1)); - // IntArray c = new IntArray((m + m + 31) / 32); - // // IntArray c = new IntArray(t + t); - // int testBit = 1 << 31; - // for (int k = 31; k >= 0; k--) { - // for (int j = 0; j < t; j++) { - // if ((m_ints[j] & testBit) != 0) { - // // The kth bit of m_ints[j] is set - // c.addShifted(b, j); - // } - // } - // testBit >>>= 1; - // if (k > 0) { - // c.shiftLeft(); - // } - // } - // return c; - // } - - // TODO note, redPol.length must be 3 for TPB and 5 for PPB - public void reduce(int m, int[] redPol) - { - for (int i = m + m - 2; i >= m; i--) + + 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); - flipBit(i); - int l = redPol.length; - while (--l >= 0) + 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) { - flipBit(redPol[l] + bit); + flipWord(ks[j] + bit, word); } } } - m_ints = resizedInts((m + 31) >> 5); } public IntArray square(int m) { - // TODO make the table static final - final int[] table = { 0x0, 0x1, 0x4, 0x5, 0x10, 0x11, 0x14, 0x15, 0x40, - 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55 }; + int len = getUsedLength(); + if (len == 0) + { + return this; + } - int t = (m + 31) >> 5; - if (m_ints.length < t) + int _2len = len << 1; + int[] r = new int[_2len]; + + int pos = 0; + while (pos < _2len) { - m_ints = resizedInts(t); + int mi = m_ints[pos >>> 1]; + r[pos++] = interleave16(mi & 0xFFFF); + r[pos++] = interleave16(mi >>> 16); } - IntArray c = new IntArray(t + t); + return new IntArray(r); + } - // TODO twice the same code, put in separate private method - for (int i = 0; i < t; i++) + private static void interleave(int[] x, int xOff, int[] z, int zOff, int count, int rounds) + { + for (int i = 0; i < count; ++i) { - int v0 = 0; - for (int j = 0; j < 4; j++) + 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) { - v0 = v0 >>> 8; - int u = (m_ints[i] >>> (j * 4)) & 0xF; - int w = table[u] << 24; - v0 |= w; + final IntArray uzCopy = uz; + uz = vz; + vz = uzCopy; + + final IntArray g1zCopy = g1z; + g1z = g2z; + g2z = g1zCopy; + + j = -j; } - c.m_ints[i + i] = v0; - v0 = 0; - int upper = m_ints[i] >>> 16; - for (int j = 0; j < 4; 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) { - v0 = v0 >>> 8; - int u = (upper >>> (j * 4)) & 0xF; - int w = table[u] << 24; - v0 |= w; + g1z.addShiftedByBits(g2z, j); } - c.m_ints[i + i + 1] = v0; } - return c; + return g2z; } public boolean equals(Object o) @@ -482,7 +822,8 @@ class IntArray int hash = 1; for (int i = 0; i < usedLen; i++) { - hash = hash * 31 + m_ints[i]; + hash *= 31; + hash ^= m_ints[i]; } return hash; } @@ -494,25 +835,26 @@ class IntArray public String toString() { - int usedLen = getUsedLength(); - if (usedLen == 0) + int i = getUsedLength(); + if (i == 0) { return "0"; } - StringBuffer sb = new StringBuffer(Integer - .toBinaryString(m_ints[usedLen - 1])); - for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--) + StringBuffer sb = new StringBuffer(Integer.toBinaryString(m_ints[--i])); + while (--i >= 0) { - String hexString = Integer.toBinaryString(m_ints[iarrJ]); + String s = Integer.toBinaryString(m_ints[i]); - // Add leading zeroes, except for highest significant int - for (int i = hexString.length(); i < 8; i++) + // Add leading zeroes, except for highest significant word + int len = s.length(); + if (len < 32) { - hexString = "0" + hexString; + sb.append(ZEROES.substring(len)); } - sb.append(hexString); + + sb.append(s); } return sb.toString(); } -} +}
\ No newline at end of file diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java b/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java new file mode 100644 index 0000000..7e8b172 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java @@ -0,0 +1,1995 @@ +package org.bouncycastle.math.ec; + +import org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/math/ec/MixedNafR2LMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/MixedNafR2LMultiplier.java new file mode 100644 index 0000000..6d5fe92 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/MixedNafR2LMultiplier.java @@ -0,0 +1,77 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/math/ec/MontgomeryLadderMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/MontgomeryLadderMultiplier.java new file mode 100644 index 0000000..cd969b5 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/MontgomeryLadderMultiplier.java @@ -0,0 +1,25 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/math/ec/NafL2RMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/NafL2RMultiplier.java new file mode 100644 index 0000000..91d91d1 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/NafL2RMultiplier.java @@ -0,0 +1,30 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/math/ec/NafR2LMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/NafR2LMultiplier.java new file mode 100644 index 0000000..aed2336 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/NafR2LMultiplier.java @@ -0,0 +1,31 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java index 804dcf7..3849858 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java @@ -2,9 +2,9 @@ package org.bouncycastle.math.ec; /** * Interface for classes storing precomputation data for multiplication - * algorithms. Used as a Memento (see GOF patterns) for - * <code>WNafMultiplier</code>. + * algorithms. Used as a Memento (see GOF patterns) by e.g. + * <code>WNafL2RMultiplier</code>. */ -interface PreCompInfo +public interface PreCompInfo { } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java index c1dd548..3601856 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java @@ -2,7 +2,7 @@ package org.bouncycastle.math.ec; import java.math.BigInteger; -class ReferenceMultiplier implements ECMultiplier +public class ReferenceMultiplier extends AbstractECMultiplier { /** * Simple shift-and-add multiplication. Serves as reference implementation @@ -13,17 +13,24 @@ class ReferenceMultiplier implements ECMultiplier * @param k The factor by which to multiply. * @return The result of the point multiplication <code>k * p</code>. */ - public ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo) + protected ECPoint multiplyPositive(ECPoint p, BigInteger k) { ECPoint q = p.getCurve().getInfinity(); int t = k.bitLength(); - for (int i = 0; i < t; i++) + if (t > 0) { - if (k.testBit(i)) + if (k.testBit(0)) { - q = q.add(p); + q = p; + } + for (int i = 1; i < t; i++) + { + p = p.twice(); + if (k.testBit(i)) + { + q = q.add(p); + } } - p = p.twice(); } return q; } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java b/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java index af4355f..42d6738 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java @@ -392,15 +392,7 @@ class Tnaf */ public static ECPoint.F2m tau(ECPoint.F2m p) { - if (p.isInfinity()) - { - return p; - } - - ECFieldElement x = p.getX(); - ECFieldElement y = p.getY(); - - return new ECPoint.F2m(p.getCurve(), x.square(), y.square(), p.isCompressed()); + return p.tau(); } /** @@ -415,23 +407,17 @@ class Tnaf */ public static byte getMu(ECCurve.F2m curve) { - BigInteger a = curve.getA().toBigInteger(); - byte mu; - - if (a.equals(ECConstants.ZERO)) - { - mu = -1; - } - else if (a.equals(ECConstants.ONE)) + if (!curve.isKoblitz()) { - mu = 1; + throw new IllegalArgumentException("No Koblitz curve (ABC), TNAF multiplication not possible"); } - else + + if (curve.getA().isZero()) { - throw new IllegalArgumentException("No Koblitz curve (ABC), " + - "TNAF multiplication not possible"); + return -1; } - return mu; + + return 1; } /** @@ -838,7 +824,9 @@ class Tnaf { pu[i] = Tnaf.multiplyFromTnaf(p, alphaTnaf[i]); } - + + p.getCurve().normalizeAll(pu); + return pu; } } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java new file mode 100644 index 0000000..59a9313 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java @@ -0,0 +1,101 @@ +package org.bouncycastle.math.ec; + +import java.math.BigInteger; + +/** + * Class implementing the WNAF (Window Non-Adjacent Form) multiplication + * algorithm. + */ +public class WNafL2RMultiplier extends AbstractECMultiplier +{ + /** + * Multiplies <code>this</code> by an integer <code>k</code> using the + * Window NAF method. + * @param k The integer by which <code>this</code> is multiplied. + * @return A new <code>ECPoint</code> which equals <code>this</code> + * multiplied by <code>k</code>. + */ + 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/bcprov/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java deleted file mode 100644 index 10c8ed2..0000000 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java +++ /dev/null @@ -1,240 +0,0 @@ -package org.bouncycastle.math.ec; - -import java.math.BigInteger; - -/** - * Class implementing the WNAF (Window Non-Adjacent Form) multiplication - * algorithm. - */ -class WNafMultiplier implements ECMultiplier -{ - /** - * Computes the Window NAF (non-adjacent Form) of an integer. - * @param width The width <code>w</code> of the Window NAF. The width is - * defined as the minimal number <code>w</code>, such that for any - * <code>w</code> 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: - * <code>k = ∑<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup> - * </code>, where the <code>k<sub>i</sub></code> denote the elements of the - * returned <code>byte[]</code>. - */ - public byte[] windowNaf(byte width, BigInteger k) - { - // The window NAF is at most 1 element longer than the binary - // representation of the integer k. byte can be used instead of short or - // int unless the window width is larger than 8. For larger width use - // short or int. However, a width of more than 8 is not efficient for - // m = log2(q) smaller than 2305 Bits. Note: Values for m larger than - // 1000 Bits are currently not used in practice. - byte[] wnaf = new byte[k.bitLength() + 1]; - - // 2^width as short and BigInteger - short pow2wB = (short)(1 << width); - BigInteger pow2wBI = BigInteger.valueOf(pow2wB); - - int i = 0; - - // The actual length of the WNAF - int length = 0; - - // while k >= 1 - while (k.signum() > 0) - { - // if k is odd - if (k.testBit(0)) - { - // k mod 2^width - BigInteger remainder = k.mod(pow2wBI); - - // if remainder > 2^(width - 1) - 1 - if (remainder.testBit(width - 1)) - { - wnaf[i] = (byte)(remainder.intValue() - pow2wB); - } - else - { - wnaf[i] = (byte)remainder.intValue(); - } - // wnaf[i] is now in [-2^(width-1), 2^(width-1)-1] - - k = k.subtract(BigInteger.valueOf(wnaf[i])); - length = i; - } - else - { - wnaf[i] = 0; - } - - // k = k/2 - k = k.shiftRight(1); - i++; - } - - length++; - - // Reduce the WNAF array to its actual length - byte[] wnafShort = new byte[length]; - System.arraycopy(wnaf, 0, wnafShort, 0, length); - return wnafShort; - } - - /** - * Multiplies <code>this</code> by an integer <code>k</code> using the - * Window NAF method. - * @param k The integer by which <code>this</code> is multiplied. - * @return A new <code>ECPoint</code> which equals <code>this</code> - * multiplied by <code>k</code>. - */ - public ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo) - { - WNafPreCompInfo wnafPreCompInfo; - - if ((preCompInfo != null) && (preCompInfo instanceof WNafPreCompInfo)) - { - wnafPreCompInfo = (WNafPreCompInfo)preCompInfo; - } - else - { - // Ignore empty PreCompInfo or PreCompInfo of incorrect type - wnafPreCompInfo = new WNafPreCompInfo(); - } - - // floor(log2(k)) - int m = k.bitLength(); - - // width of the Window NAF - byte width; - - // Required length of precomputation array - int reqPreCompLen; - - // Determine optimal width and corresponding length of precomputation - // array based on literature values - if (m < 13) - { - width = 2; - reqPreCompLen = 1; - } - else - { - if (m < 41) - { - width = 3; - reqPreCompLen = 2; - } - else - { - if (m < 121) - { - width = 4; - reqPreCompLen = 4; - } - else - { - if (m < 337) - { - width = 5; - reqPreCompLen = 8; - } - else - { - if (m < 897) - { - width = 6; - reqPreCompLen = 16; - } - else - { - if (m < 2305) - { - width = 7; - reqPreCompLen = 32; - } - else - { - width = 8; - reqPreCompLen = 127; - } - } - } - } - } - } - - // The length of the precomputation array - int preCompLen = 1; - - ECPoint[] preComp = wnafPreCompInfo.getPreComp(); - ECPoint twiceP = wnafPreCompInfo.getTwiceP(); - - // Check if the precomputed ECPoints already exist - if (preComp == null) - { - // Precomputation must be performed from scratch, create an empty - // precomputation array of desired length - preComp = new ECPoint[]{ p }; - } - else - { - // Take the already precomputed ECPoints to start with - preCompLen = preComp.length; - } - - if (twiceP == null) - { - // Compute twice(p) - twiceP = p.twice(); - } - - if (preCompLen < reqPreCompLen) - { - // Precomputation array must be made bigger, copy existing preComp - // array into the larger new preComp array - ECPoint[] oldPreComp = preComp; - preComp = new ECPoint[reqPreCompLen]; - System.arraycopy(oldPreComp, 0, preComp, 0, preCompLen); - - 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]); - } - } - - // Compute the Window NAF of the desired width - byte[] wnaf = windowNaf(width, k); - int l = wnaf.length; - - // Apply the Window NAF to p using the precomputed ECPoint values. - ECPoint q = p.getCurve().getInfinity(); - for (int i = l - 1; i >= 0; i--) - { - q = q.twice(); - - if (wnaf[i] != 0) - { - if (wnaf[i] > 0) - { - q = q.add(preComp[(wnaf[i] - 1)/2]); - } - else - { - // wnaf[i] < 0 - q = q.subtract(preComp[(-wnaf[i] - 1)/2]); - } - } - } - - // Set PreCompInfo in ECPoint, such that it is available for next - // multiplication. - wnafPreCompInfo.setPreComp(preComp); - wnafPreCompInfo.setTwiceP(twiceP); - p.setPreCompInfo(wnafPreCompInfo); - return q; - } - -} diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java index fc0d5fe..d142ab7 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java @@ -4,21 +4,23 @@ package org.bouncycastle.math.ec; * Class holding precomputation data for the WNAF (Window Non-Adjacent Form) * algorithm. */ -class WNafPreCompInfo implements PreCompInfo +public class WNafPreCompInfo implements PreCompInfo { /** - * Array holding the precomputed <code>ECPoint</code>s used for the Window - * NAF multiplication in <code> - * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply() - * WNafMultiplier.multiply()}</code>. + * Array holding the precomputed <code>ECPoint</code>s used for a Window + * NAF multiplication. */ private ECPoint[] preComp = null; /** + * Array holding the negations of the precomputed <code>ECPoint</code>s used + * for a Window NAF multiplication. + */ + private ECPoint[] preCompNeg = null; + + /** * Holds an <code>ECPoint</code> representing twice(this). Used for the - * Window NAF multiplication in <code> - * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply() - * WNafMultiplier.multiply()}</code>. + * Window NAF multiplication to create or extend the precomputed values. */ private ECPoint twiceP = null; @@ -27,18 +29,28 @@ class WNafPreCompInfo implements PreCompInfo 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 twiceThis) + protected void setTwiceP(ECPoint twiceP) { - this.twiceP = twiceThis; + this.twiceP = twiceP; } } diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java new file mode 100644 index 0000000..6465d66 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java @@ -0,0 +1,393 @@ +package org.bouncycastle.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 <code>w</code> of the Window NAF. The width is + * defined as the minimal number <code>w</code>, such that for any + * <code>w</code> 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: + * <code>k = ∑<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup> + * </code>, where the <code>k<sub>i</sub></code> denote the elements of the + * returned <code>byte[]</code>. + */ + 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/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java index 2353979..7bd30ec 100644 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java @@ -6,7 +6,7 @@ import java.math.BigInteger; * Class implementing the WTNAF (Window * <code>τ</code>-adic Non-Adjacent Form) algorithm. */ -class WTauNafMultiplier implements ECMultiplier +public class WTauNafMultiplier extends AbstractECMultiplier { /** * Multiplies a {@link org.bouncycastle.math.ec.ECPoint.F2m ECPoint.F2m} @@ -16,7 +16,7 @@ class WTauNafMultiplier implements ECMultiplier * @param k The integer by which to multiply <code>k</code>. * @return <code>p</code> multiplied by <code>k</code>. */ - public ECPoint multiply(ECPoint point, BigInteger k, PreCompInfo preCompInfo) + protected ECPoint multiplyPositive(ECPoint point, BigInteger k) { if (!(point instanceof ECPoint.F2m)) { @@ -25,8 +25,7 @@ class WTauNafMultiplier implements ECMultiplier } ECPoint.F2m p = (ECPoint.F2m)point; - - ECCurve.F2m curve = (ECCurve.F2m) p.getCurve(); + ECCurve.F2m curve = (ECCurve.F2m)p.getCurve(); int m = curve.getM(); byte a = curve.getA().toBigInteger().byteValue(); byte mu = curve.getMu(); @@ -34,7 +33,7 @@ class WTauNafMultiplier implements ECMultiplier ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10); - return multiplyWTnaf(p, rho, preCompInfo, a, mu); + return multiplyWTnaf(p, rho, curve.getPreCompInfo(p), a, mu); } /** @@ -88,7 +87,7 @@ class WTauNafMultiplier implements ECMultiplier if ((preCompInfo == null) || !(preCompInfo instanceof WTauNafPreCompInfo)) { pu = Tnaf.getPreComp(p, a); - p.setPreCompInfo(new WTauNafPreCompInfo(pu)); + curve.setPreCompInfo(p, new WTauNafPreCompInfo(pu)); } else { diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitL2RMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitL2RMultiplier.java new file mode 100644 index 0000000..b478dc7 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitL2RMultiplier.java @@ -0,0 +1,29 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitR2LMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitR2LMultiplier.java new file mode 100644 index 0000000..baa702f --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitR2LMultiplier.java @@ -0,0 +1,30 @@ +package org.bouncycastle.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/bcprov/src/main/java/org/bouncycastle/math/ec/package.html b/bcprov/src/main/java/org/bouncycastle/math/ec/package.html deleted file mode 100644 index a02605b..0000000 --- a/bcprov/src/main/java/org/bouncycastle/math/ec/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Math support for Elliptic Curve. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/ocsp/package.html b/bcprov/src/main/java/org/bouncycastle/ocsp/package.html deleted file mode 100644 index 2498f2e..0000000 --- a/bcprov/src/main/java/org/bouncycastle/ocsp/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -<b>Deprecated</b>: see the bcpkix distribution (org.bouncycastle.cert.ocsp), classes for dealing Online Certificate Status Protocol (OCSP) - RFC 2560. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java index b97a8f3..d93d995 100644 --- a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java +++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java @@ -2,26 +2,45 @@ package org.bouncycastle.pqc.asn1; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +/** + * PQC: + * <p> + * { 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"); - public static final ASN1ObjectIdentifier rainbowWithSha1 = rainbow.branch("1"); + /** 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"); - public static final ASN1ObjectIdentifier gmssWithSha1 = gmss.branch("1"); + /** 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"); - 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.1 */ + public static final ASN1ObjectIdentifier mcEliece = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.1"); - public static final ASN1ObjectIdentifier mcElieceCca2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.2"); + /** 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/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java index 51aa026..340f032 100644 --- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java +++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java @@ -21,7 +21,7 @@ public class BouncyCastlePQCProvider extends Provider implements ConfigurableProvider { - private static String info = "BouncyCastle Post-Quantum Security Provider v1.48"; + private static String info = "BouncyCastle Post-Quantum Security Provider v1.50"; public static String PROVIDER_NAME = "BCPQC"; @@ -46,7 +46,7 @@ public class BouncyCastlePQCProvider */ public BouncyCastlePQCProvider() { - super(PROVIDER_NAME, 1.48, info); + super(PROVIDER_NAME, 1.50, info); AccessController.doPrivileged(new PrivilegedAction() { diff --git a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java index 457320e..3f7677c 100644 --- a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java +++ b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java @@ -11,7 +11,7 @@ public final class Arrays { // static class, hide constructor } - + public static boolean areEqual( boolean[] a, boolean[] b) @@ -199,36 +199,62 @@ public final class Arrays return true; } - public static boolean areEqual( - BigInteger[] a, - BigInteger[] b) + 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++) { - if (!a[i].equals(b[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) @@ -391,7 +417,7 @@ public final class Arrays return hc; } - public static int hashCode(BigInteger[] data) + public static int hashCode(Object[] data) { if (data == null) { @@ -423,6 +449,20 @@ public final class Arrays return copy; } + public static byte[] clone(byte[] data, byte[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.length != data.length)) + { + return clone(data); + } + System.arraycopy(data, 0, existing, 0, existing.length); + return existing; + } + public static byte[][] clone(byte[][] data) { if (data == null) @@ -470,6 +510,33 @@ public final class Arrays return copy; } + public static long[] clone(long[] data) + { + if (data == null) + { + return null; + } + long[] copy = new long[data.length]; + + System.arraycopy(data, 0, copy, 0, data.length); + + return copy; + } + + public static long[] clone(long[] data, long[] existing) + { + if (data == null) + { + return null; + } + if ((existing == null) || (existing.length != data.length)) + { + return clone(data); + } + System.arraycopy(data, 0, existing, 0, existing.length); + return existing; + } + public static short[] clone(short[] data) { if (data == null) @@ -576,6 +643,17 @@ public final class Arrays 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); @@ -660,6 +738,34 @@ public final class Arrays 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) @@ -733,4 +839,18 @@ public final class Arrays 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/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java b/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java index e2fe590..f7f7e68 100644 --- a/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java +++ b/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java @@ -40,43 +40,25 @@ public final class BigIntegers * @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) + public static byte[] asUnsignedByteArray(int length, BigInteger value) { byte[] bytes = value.toByteArray(); - - if (bytes[0] == 0) + if (bytes.length == length) { - if (bytes.length - 1 > length) - { - throw new IllegalArgumentException("standard length exceeded for value"); - } - - byte[] tmp = new byte[length]; - - System.arraycopy(bytes, 1, tmp, tmp.length - (bytes.length - 1), bytes.length - 1); - - return tmp; + return bytes; } - else - { - if (bytes.length == length) - { - return bytes; - } - - if (bytes.length > length) - { - throw new IllegalArgumentException("standard length exceeded for value"); - } - byte[] tmp = new byte[length]; + int start = bytes[0] == 0 ? 1 : 0; + int count = bytes.length - start; - System.arraycopy(bytes, 0, tmp, tmp.length - bytes.length, bytes.length); - - return tmp; + 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; } /** @@ -120,4 +102,20 @@ public final class BigIntegers // 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/bcprov/src/main/java/org/bouncycastle/util/Shorts.java b/bcprov/src/main/java/org/bouncycastle/util/Shorts.java new file mode 100644 index 0000000..258e01e --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/util/Shorts.java @@ -0,0 +1,9 @@ +package org.bouncycastle.util; + +public class Shorts +{ + public static Short valueOf(short value) + { + return Short.valueOf(value); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/package.html b/bcprov/src/main/java/org/bouncycastle/util/encoders/package.html deleted file mode 100644 index 3be222b..0000000 --- a/bcprov/src/main/java/org/bouncycastle/util/encoders/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -Classes for producing and reading Base64 and Hex strings. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/util/io/BufferingOutputStream.java b/bcprov/src/main/java/org/bouncycastle/util/io/BufferingOutputStream.java new file mode 100644 index 0000000..9d5fe14 --- /dev/null +++ b/bcprov/src/main/java/org/bouncycastle/util/io/BufferingOutputStream.java @@ -0,0 +1,108 @@ +package org.bouncycastle.util.io; + +import java.io.IOException; +import java.io.OutputStream; + +import org.bouncycastle.util.Arrays; + +/** + * An output stream that buffers data to be feed into an encapsulated output stream. + * <p> + * The stream zeroes out the internal buffer on each flush. + * </p> + */ +public class BufferingOutputStream + extends OutputStream +{ + private final OutputStream other; + private final byte[] buf; + + private int bufOff; + + /** + * Create a buffering stream with the default buffer size (4096). + * + * @param other output stream to be wrapped. + */ + public BufferingOutputStream(OutputStream other) + { + this.other = other; + this.buf = new byte[4096]; + } + + /** + * Create a buffering stream with a specified buffer size. + * + * @param other output stream to be wrapped. + * @param bufferSize size in bytes for internal buffer. + */ + public BufferingOutputStream(OutputStream other, int bufferSize) + { + this.other = other; + this.buf = new byte[bufferSize]; + } + + public void write(byte[] bytes, int offset, int len) + throws IOException + { + if (len < buf.length - bufOff) + { + System.arraycopy(bytes, offset, buf, bufOff, len); + bufOff += len; + } + else + { + int gap = buf.length - bufOff; + + System.arraycopy(bytes, offset, buf, bufOff, gap); + bufOff += gap; + + flush(); + + offset += gap; + len -= gap; + while (len >= buf.length) + { + other.write(bytes, offset, buf.length); + offset += buf.length; + len -= buf.length; + } + + if (len > 0) + { + System.arraycopy(bytes, offset, buf, bufOff, len); + bufOff += len; + } + } + } + + public void write(int b) + throws IOException + { + buf[bufOff++] = (byte)b; + if (bufOff == buf.length) + { + flush(); + } + } + + /** + * Flush the internal buffer to the encapsulated output stream. Zero the buffer contents when done. + * + * @throws IOException on error. + */ + public void flush() + throws IOException + { + other.write(buf, 0, bufOff); + bufOff = 0; + Arrays.fill(buf, (byte)0); + } + + public void close() + throws IOException + { + flush(); + other.close(); + } +} diff --git a/bcprov/src/main/java/org/bouncycastle/x509/X509Store.java b/bcprov/src/main/java/org/bouncycastle/x509/X509Store.java index 1bfc00f..61d921c 100644 --- a/bcprov/src/main/java/org/bouncycastle/x509/X509Store.java +++ b/bcprov/src/main/java/org/bouncycastle/x509/X509Store.java @@ -1,13 +1,16 @@ package org.bouncycastle.x509; -import org.bouncycastle.util.Selector; -import org.bouncycastle.util.Store; - import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; import java.util.Collection; +import org.bouncycastle.util.Selector; +import org.bouncycastle.util.Store; + +/** + * @deprecated use CollectionStore - this class will be removed. + */ public class X509Store implements Store { diff --git a/bcprov/src/main/java/org/bouncycastle/x509/examples/package.html b/bcprov/src/main/java/org/bouncycastle/x509/examples/package.html deleted file mode 100644 index 6262157..0000000 --- a/bcprov/src/main/java/org/bouncycastle/x509/examples/package.html +++ /dev/null @@ -1,7 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -<p> -Examples for X.509 attribute certificates. -<p> -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/x509/extension/package.html b/bcprov/src/main/java/org/bouncycastle/x509/extension/package.html deleted file mode 100644 index 8127aa5..0000000 --- a/bcprov/src/main/java/org/bouncycastle/x509/extension/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -<b>Deprecated:</b> see bcpkix distribution (org.bouncycastle.cert), helper classes for dealing with common X.509 extensions. -</body> -</html> diff --git a/bcprov/src/main/java/org/bouncycastle/x509/package.html b/bcprov/src/main/java/org/bouncycastle/x509/package.html deleted file mode 100644 index be27c55..0000000 --- a/bcprov/src/main/java/org/bouncycastle/x509/package.html +++ /dev/null @@ -1,7 +0,0 @@ -<html> -<body bgcolor="#ffffff"> -<p> -<b>Deprecated:</b> see bcpkix distribution (org.bouncycastle.cert), classes for supporting the generation of X.509 certificates and X.509 attribute certificates. -<p> -</body> -</html> |