--- a/jdk/src/java.base/share/classes/sun/security/provider/AbstractDrbg.java Wed Jul 06 01:20:20 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/AbstractDrbg.java Wed Jul 06 21:52:12 2016 +0800
@@ -377,11 +377,12 @@
instantiateIfNecessary(null);
- // Step 7: Auto reseed
+ // Step 7: Auto reseed (reseedCounter might overflow)
// Double checked locking, safe because reseedCounter is volatile
- if (reseedCounter > reseedInterval || pr) {
+ if (reseedCounter < 0 || reseedCounter > reseedInterval || pr) {
synchronized (this) {
- if (reseedCounter > reseedInterval || pr) {
+ if (reseedCounter < 0 || reseedCounter > reseedInterval
+ || pr) {
reseedAlgorithm(getEntropyInput(pr), ai);
ai = null;
}
--- a/jdk/src/java.base/share/classes/sun/security/provider/AbstractHashDrbg.java Wed Jul 06 01:20:20 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/AbstractHashDrbg.java Wed Jul 06 21:52:12 2016 +0800
@@ -27,7 +27,8 @@
import sun.security.util.HexDumpEncoder;
-import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
public abstract class AbstractHashDrbg extends AbstractDrbg {
@@ -113,16 +114,13 @@
// 800-90Ar1 10.1.2.3: Hmac_DRBG Instantiate Process.
// Step 1: entropy_input || nonce || personalization_string.
- byte[] seed = Arrays.copyOf(entropy, entropy.length + nonce.length +
- ((personalizationString == null) ? 0
- : personalizationString.length));
- System.arraycopy(nonce, 0, seed, entropy.length, nonce.length);
+ List<byte[]> inputs = new ArrayList<>(3);
+ inputs.add(entropy);
+ inputs.add(nonce);
if (personalizationString != null) {
- System.arraycopy(personalizationString, 0,
- seed, entropy.length + nonce.length,
- personalizationString.length);
+ inputs.add(personalizationString);
}
- hashReseedInternal(seed);
+ hashReseedInternal(inputs);
}
@Override
@@ -140,13 +138,17 @@
// 800-90Ar1 10.1.2.4: Hmac_DRBG Reseed Process.
// Step 1: entropy_input || additional_input.
+ List<byte[]> inputs = new ArrayList<>(2);
+ inputs.add(ei);
if (additionalInput != null) {
- ei = Arrays.copyOf(ei, ei.length + additionalInput.length);
- System.arraycopy(additionalInput, 0, ei,
- ei.length - additionalInput.length, additionalInput.length);
+ inputs.add(additionalInput);
}
- hashReseedInternal(ei);
+ hashReseedInternal(inputs);
}
- protected abstract void hashReseedInternal(byte[] seed);
+ /**
+ * Operates on multiple inputs.
+ * @param inputs not null, each element neither null
+ */
+ protected abstract void hashReseedInternal(List<byte[]> inputs);
}
--- a/jdk/src/java.base/share/classes/sun/security/provider/CtrDrbg.java Wed Jul 06 01:20:20 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/CtrDrbg.java Wed Jul 06 21:52:12 2016 +0800
@@ -242,6 +242,11 @@
if (personalizationString == null) {
more = nonce;
} else {
+ if (nonce.length + personalizationString.length < 0) {
+ // Length must be represented as a 32 bit integer in df()
+ throw new IllegalArgumentException(
+ "nonce plus personalization string is too long");
+ }
more = Arrays.copyOf(
nonce, nonce.length + personalizationString.length);
System.arraycopy(personalizationString, 0, more, nonce.length,
@@ -256,50 +261,73 @@
reseedAlgorithm(ei, more);
}
+ /**
+ * Block_cipher_df in 10.3.2
+ *
+ * @param input the input string
+ * @return the output block (always of seedLen)
+ */
private byte[] df(byte[] input) {
+ // 800-90Ar1 10.3.2
+ // 2. L = len (input_string)/8
int l = input.length;
+ // 3. N = number_of_bits_to_return/8
int n = seedLen;
- int slen = 4 + 4 + l + 1;
- byte[] s = new byte[(slen + blockLen - 1) / blockLen * blockLen];
- s[0] = (byte)(l >> 24);
- s[1] = (byte)(l >> 16);
- s[2] = (byte)(l >> 8);
- s[3] = (byte)(l);
- s[4] = (byte)(n >> 24);
- s[5] = (byte)(n >> 16);
- s[6] = (byte)(n >> 8);
- s[7] = (byte)(n);
- System.arraycopy(input, 0, s, 8, l);
- s[8+l] = (byte)0x80;
+ // 4. S = L || N || input_string || 0x80
+ byte[] ln = new byte[8];
+ ln[0] = (byte)(l >> 24);
+ ln[1] = (byte)(l >> 16);
+ ln[2] = (byte)(l >> 8);
+ ln[3] = (byte)(l);
+ ln[4] = (byte)(n >> 24);
+ ln[5] = (byte)(n >> 16);
+ ln[6] = (byte)(n >> 8);
+ ln[7] = (byte)(n);
+ // 5. Zero padding of S
+ // Not necessary, see bcc
+
+ // 8. K = leftmost (0x00010203...1D1E1F, keylen).
byte[] k = new byte[keyLen];
for (int i = 0; i < k.length; i++) {
k[i] = (byte)i;
}
+ // 6. temp = the Null String
byte[] temp = new byte[seedLen];
+ // 7. i = 0
for (int i = 0; i * blockLen < temp.length; i++) {
- byte[] iv = new byte[blockLen + s.length];
+ // 9.1 IV = i || 0^(outlen - len (i)). outLen is blockLen
+ byte[] iv = new byte[blockLen];
iv[0] = (byte)(i >> 24);
iv[1] = (byte)(i >> 16);
iv[2] = (byte)(i >> 8);
iv[3] = (byte)(i);
- System.arraycopy(s, 0, iv, blockLen, s.length);
+
int tailLen = temp.length - blockLen*i;
if (tailLen > blockLen) {
tailLen = blockLen;
}
- System.arraycopy(bcc(k, iv), 0, temp, blockLen*i, tailLen);
+ // 9.2 temp = temp || BCC (K, (IV || S)).
+ System.arraycopy(bcc(k, iv, ln, input, new byte[]{(byte)0x80}),
+ 0, temp, blockLen*i, tailLen);
}
+ // 10. K = leftmost(temp, keylen)
k = Arrays.copyOf(temp, keyLen);
+
+ // 11. x = select(temp, keylen+1, keylen+outlen)
byte[] x = Arrays.copyOfRange(temp, keyLen, temp.length);
+ // 12. temp = the Null string
+ // No need to clean up, temp will be overwritten
+
for (int i = 0; i * blockLen < seedLen; i++) {
try {
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
int tailLen = temp.length - blockLen*i;
+ // 14. requested_bits = leftmost(temp, nuumber_of_bits_to_return)
if (tailLen > blockLen) {
tailLen = blockLen;
}
@@ -309,21 +337,45 @@
throw new InternalError(e);
}
}
+
+ // 15. Return
return temp;
}
- private byte[] bcc(byte[] k, byte[] data) {
+ /**
+ * Block_Encrypt in 10.3.3
+ *
+ * @param k the key
+ * @param data after concatenated, the data to be operated upon. This is
+ * a series of byte[], each with an arbitrary length. Note
+ * that the full length is not necessarily a multiple of
+ * outlen. XOR with zero is no-op.
+ * @return the result
+ */
+ private byte[] bcc(byte[] k, byte[]... data) {
byte[] chain = new byte[blockLen];
- int n = data.length / blockLen;
- for (int i = 0; i < n; i++) {
- byte[] inputBlock = Arrays.copyOfRange(
- data, i * blockLen, i * blockLen + blockLen);
- for (int j = 0; j < blockLen; j++) {
- inputBlock[j] ^= chain[j];
+ int n1 = 0; // index in data
+ int n2 = 0; // index in data[n1]
+ // pack blockLen of bytes into chain from data[][], again and again
+ while (n1 < data.length) {
+ int j;
+ out: for (j = 0; j < blockLen; j++) {
+ while (n2 >= data[n1].length) {
+ n1++;
+ if (n1 >= data.length) {
+ break out;
+ }
+ n2 = 0;
+ }
+ chain[j] ^= data[n1][n2];
+ n2++;
+ }
+ if (j == 0) { // all data happens to be consumed in the last loop
+ break;
}
try {
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(k, keyAlg));
- chain = cipher.doFinal(inputBlock);
+ chain = cipher.doFinal(chain);
} catch (GeneralSecurityException e) {
throw new InternalError(e);
}
@@ -341,6 +393,11 @@
// Step 1: cat bytes
if (additionalInput != null) {
+ if (ei.length + additionalInput.length < 0) {
+ // Length must be represented as a 32 bit integer in df()
+ throw new IllegalArgumentException(
+ "entropy plus additional input is too long");
+ }
byte[] temp = Arrays.copyOf(
ei, ei.length + additionalInput.length);
System.arraycopy(additionalInput, 0, temp, ei.length,
@@ -430,10 +487,10 @@
// Step 3. temp = Null
int pos = 0;
+ int len = result.length;
// Step 4. Loop
- while (pos < result.length) {
- int tailLen = result.length - pos;
+ while (len > 0) {
// Step 4.1. Increment
addOne(v, ctrLen);
try {
@@ -443,10 +500,15 @@
// Step 4.3 and 5. Cat bytes and leftmost
System.arraycopy(out, 0, result, pos,
- (tailLen > blockLen) ? blockLen : tailLen);
+ (len > blockLen) ? blockLen : len);
} catch (GeneralSecurityException e) {
throw new InternalError(e);
}
+ len -= blockLen;
+ if (len <= 0) {
+ // shortcut, so that pos needn't be updated
+ break;
+ }
pos += blockLen;
}
--- a/jdk/src/java.base/share/classes/sun/security/provider/HashDrbg.java Wed Jul 06 01:20:20 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/HashDrbg.java Wed Jul 06 21:52:12 2016 +0800
@@ -31,7 +31,9 @@
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandomParameters;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
public class HashDrbg extends AbstractHashDrbg {
@@ -70,7 +72,7 @@
}
}
- private byte[] hashDf(int requested, byte[]... inputs) {
+ private byte[] hashDf(int requested, List<byte[]> inputs) {
return hashDf(digest, outLen, requested, inputs);
}
@@ -79,6 +81,9 @@
* The function is used inside Hash_DRBG, and can also be used as an
* approved conditioning function as described in 800-90B 6.4.2.2.
*
+ * Note: In each current call, requested is seedLen, therefore small,
+ * no need to worry about overflow.
+ *
* @param digest a {@code MessageDigest} object in reset state
* @param outLen {@link MessageDigest#getDigestLength} of {@code digest}
* @param requested requested output length, in bytes
@@ -86,12 +91,18 @@
* @return the condensed/expanded output
*/
public static byte[] hashDf(MessageDigest digest, int outLen,
- int requested, byte[]... inputs) {
+ int requested, List<byte[]> inputs) {
+ // 1. temp = the Null string.
+ // 2. len = upper_int(no_of_bits_to_return / outLen)
int len = (requested + outLen - 1) / outLen;
byte[] temp = new byte[len * outLen];
+ // 3. counter = 0x01
int counter = 1;
+ // 4. For i = 1 to len do
for (int i=0; i<len; i++) {
+ // 4.1 temp = temp
+ // || Hash (counter || no_of_bits_to_return || input_string).
digest.update((byte) counter);
digest.update((byte)(requested >> 21)); // requested*8 as int32
digest.update((byte)(requested >> 13));
@@ -105,14 +116,17 @@
} catch (DigestException e) {
throw new AssertionError("will not happen", e);
}
+ // 4.2 counter = counter + 1
counter++;
}
+ // 5. requested_bits = leftmost (temp, no_of_bits_to_return).
return temp.length == requested? temp: Arrays.copyOf(temp, requested);
+ // 6. Return
}
// This method is used by both instantiation and reseeding.
@Override
- protected final synchronized void hashReseedInternal(byte[] input) {
+ protected final synchronized void hashReseedInternal(List<byte[]> inputs) {
// 800-90Ar1 10.1.1.2: Instantiate Process.
// 800-90Ar1 10.1.1.3: Reseed Process.
@@ -121,16 +135,21 @@
// Step 2: seed = Hash_df (seed_material, seedlen).
if (v != null) {
// Step 1 of 10.1.1.3: Prepend 0x01 || V
- seed = hashDf(seedLen, ONE, v, input);
+ inputs.add(0, ONE);
+ inputs.add(1, v);
+ seed = hashDf(seedLen, inputs);
} else {
- seed = hashDf(seedLen, input);
+ seed = hashDf(seedLen, inputs);
}
// Step 3. V = seed.
v = seed;
// Step 4. C = Hash_df ((0x00 || V), seedlen).
- c = hashDf(seedLen, ZERO, v);
+ inputs = new ArrayList<>(2);
+ inputs.add(ZERO);
+ inputs.add(v);
+ c = hashDf(seedLen, inputs);
// Step 5. reseed_counter = 1.
reseedCounter = 1;
@@ -197,7 +216,7 @@
}
// Step 3. Hashgen (requested_number_of_bits, V).
- hashGen(result, result.length, v);
+ hashGen(result, v);
// Step 4. H = Hash (0x03 || V).
digest.update((byte)3);
@@ -222,10 +241,7 @@
}
// 800-90Ar1 10.1.1.4: Hashgen
- private void hashGen(byte[] output, int len, byte[] v) {
-
- // Step 1. m
- int m = (len + outLen - 1) / outLen;
+ private void hashGen(byte[] output, byte[] v) {
// Step 2. data = V
byte[] data = v;
@@ -233,32 +249,36 @@
// Step 3: W is output not filled
// Step 4: For i = 1 to m
- for (int i = 0; i < m; i++) {
- int tailLen = len - i * outLen;
- if (tailLen < outLen) {
+ int pos = 0;
+ int len = output.length;
+
+ while (len > 0) {
+ if (len < outLen) {
// Step 4.1 w = Hash (data).
// Step 4.2 W = W || w.
- System.arraycopy(digest.digest(data), 0, output, i * outLen,
- tailLen);
+ System.arraycopy(digest.digest(data), 0, output, pos,
+ len);
} else {
try {
// Step 4.1 w = Hash (data).
digest.update(data);
// Step 4.2 digest into right position, no need to cat
- digest.digest(output, i*outLen, outLen);
+ digest.digest(output, pos, outLen);
} catch (DigestException e) {
throw new AssertionError("will not happen", e);
}
}
- // Unless this is the last around, we will need to increment data.
- // but we cannot change v, so a copy is made.
- if (i != m - 1) {
- if (data == v) {
- data = Arrays.copyOf(v, v.length);
- }
- // Step 4.3 data = (data + 1) mod 2^seedlen.
- addBytes(data, seedLen, ONE);
+ len -= outLen;
+ if (len <= 0) {
+ // shortcut, so that data and pos needn't be updated
+ break;
}
+ // Step 4.3 data = (data + 1) mod 2^seedlen.
+ if (data == v) {
+ data = Arrays.copyOf(v, v.length);
+ }
+ addBytes(data, seedLen, ONE);
+ pos += outLen;
}
// Step 5: No need to truncate
--- a/jdk/src/java.base/share/classes/sun/security/provider/HmacDrbg.java Wed Jul 06 01:20:20 2016 -0700
+++ b/jdk/src/java.base/share/classes/sun/security/provider/HmacDrbg.java Wed Jul 06 21:52:12 2016 +0800
@@ -32,6 +32,8 @@
import java.security.NoSuchProviderException;
import java.security.SecureRandomParameters;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
public class HmacDrbg extends AbstractHashDrbg {
@@ -56,7 +58,7 @@
}
// 800-90Ar1 10.1.2.2: HMAC_DRBG Update Process
- private void update(byte[]... inputs) {
+ private void update(List<byte[]> inputs) {
try {
// Step 1. K = HMAC (K, V || 0x00 || provided_data).
mac.init(new SecretKeySpec(k, macAlg));
@@ -71,7 +73,7 @@
mac.init(new SecretKeySpec(k, macAlg));
v = mac.doFinal(v);
- if (inputs.length != 0) {
+ if (!inputs.isEmpty()) {
// Step 4. K = HMAC (K, V || 0x01 || provided_data).
mac.update(v);
mac.update((byte) 1);
@@ -116,7 +118,7 @@
// This method is used by both instantiation and reseeding.
@Override
- protected final synchronized void hashReseedInternal(byte[] input) {
+ protected final synchronized void hashReseedInternal(List<byte[]> input) {
// 800-90Ar1 10.1.2.3: Instantiate Process.
// 800-90Ar1 10.1.2.4: Reseed Process.
@@ -156,16 +158,15 @@
// Step 2. HMAC_DRBG_Update
if (additionalInput != null) {
- update(additionalInput);
+ update(Collections.singletonList(additionalInput));
}
// Step 3. temp = Null.
int pos = 0;
+ int len = result.length;
// Step 4. Loop
- while (pos < result.length) {
- int tailLen = result.length - pos;
-
+ while (len > 0) {
// Step 4.1 V = HMAC (Key, V).
try {
mac.init(new SecretKeySpec(k, macAlg));
@@ -175,7 +176,13 @@
v = mac.doFinal(v);
// Step 4.2 temp = temp || V.
System.arraycopy(v, 0, result, pos,
- tailLen > outLen ? outLen : tailLen);
+ len > outLen ? outLen : len);
+
+ len -= outLen;
+ if (len <= 0) {
+ // shortcut, so that pos needn't be updated
+ break;
+ }
pos += outLen;
}
@@ -183,9 +190,9 @@
// Step 6. HMAC_DRBG_Update (additional_input, Key, V).
if (additionalInput != null) {
- update(additionalInput);
+ update(Collections.singletonList(additionalInput));
} else {
- update();
+ update(Collections.emptyList());
}
// Step 7. reseed_counter = reseed_counter + 1.