mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-26 23:00:38 +00:00
8158589: Possible integer overflow issues for DRBG
Reviewed-by: xuelei, mullan
This commit is contained in:
parent
6e722043e3
commit
db8e727862
@ -377,11 +377,12 @@ public abstract class AbstractDrbg {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -27,7 +27,8 @@ package sun.security.provider;
|
||||
|
||||
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 @@ public abstract class AbstractHashDrbg extends AbstractDrbg {
|
||||
// 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 @@ public abstract class AbstractHashDrbg extends AbstractDrbg {
|
||||
// 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);
|
||||
}
|
||||
|
||||
@ -242,6 +242,11 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
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 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
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 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
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 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
|
||||
// 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 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
|
||||
// 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 @@ public class CtrDrbg extends AbstractDrbg {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@ -31,7 +31,9 @@ import java.security.MessageDigest;
|
||||
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 @@ public class HashDrbg extends AbstractHashDrbg {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class HashDrbg extends AbstractHashDrbg {
|
||||
* 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 @@ public class HashDrbg extends AbstractHashDrbg {
|
||||
* @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 @@ public class HashDrbg extends AbstractHashDrbg {
|
||||
} 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 @@ public class HashDrbg extends AbstractHashDrbg {
|
||||
// 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 @@ public class HashDrbg extends AbstractHashDrbg {
|
||||
}
|
||||
|
||||
// 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 @@ public class HashDrbg extends AbstractHashDrbg {
|
||||
}
|
||||
|
||||
// 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 @@ public class HashDrbg extends AbstractHashDrbg {
|
||||
// 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
|
||||
|
||||
@ -32,6 +32,8 @@ import java.security.NoSuchAlgorithmException;
|
||||
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 @@ public class HmacDrbg extends AbstractHashDrbg {
|
||||
}
|
||||
|
||||
// 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 @@ public class HmacDrbg extends AbstractHashDrbg {
|
||||
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 @@ public class HmacDrbg extends AbstractHashDrbg {
|
||||
|
||||
// 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 @@ public class HmacDrbg extends AbstractHashDrbg {
|
||||
|
||||
// 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 @@ public class HmacDrbg extends AbstractHashDrbg {
|
||||
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 @@ public class HmacDrbg extends AbstractHashDrbg {
|
||||
|
||||
// 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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user