Skip to content

Commit ec1d338

Browse files
Valerie Pengslowhog
authored andcommitted
8272243: Improve DER parsing
Reviewed-by: weijun, rhalade
1 parent c2cbeb3 commit ec1d338

File tree

3 files changed

+111
-55
lines changed

3 files changed

+111
-55
lines changed

src/java.base/share/classes/sun/security/util/BitArray.java

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,32 @@ public BitArray(int length) throws IllegalArgumentException {
6565
repn = new byte[(length + BITS_PER_UNIT - 1)/BITS_PER_UNIT];
6666
}
6767

68-
6968
/**
7069
* Creates a BitArray of the specified size, initialized from the
71-
* specified byte array. The most significant bit of {@code a[0]} gets
72-
* index zero in the BitArray. The array a must be large enough
73-
* to specify a value for every bit in the BitArray. In other words,
74-
* {@code 8*a.length <= length}.
70+
* specified byte array. The most significant bit of {@code a[0]} gets
71+
* index zero in the BitArray. The array must be large enough to specify
72+
* a value for every bit of the BitArray. i.e. {@code 8*a.length <= length}.
7573
*/
7674
public BitArray(int length, byte[] a) throws IllegalArgumentException {
75+
this(length, a, 0);
76+
}
77+
78+
/**
79+
* Creates a BitArray of the specified size, initialized from the
80+
* specified byte array starting at the specified offset. The most
81+
* significant bit of {@code a[ofs]} gets index zero in the BitArray.
82+
* The array must be large enough to specify a value for every bit of
83+
* the BitArray, i.e. {@code 8*(a.length - ofs) <= length}.
84+
*/
85+
public BitArray(int length, byte[] a, int ofs)
86+
throws IllegalArgumentException {
7787

7888
if (length < 0) {
7989
throw new IllegalArgumentException("Negative length for BitArray");
8090
}
81-
if (a.length * BITS_PER_UNIT < length) {
82-
throw new IllegalArgumentException("Byte array too short to represent " +
83-
"bit array of given length");
91+
if ((a.length - ofs) * BITS_PER_UNIT < length) {
92+
throw new IllegalArgumentException
93+
("Byte array too short to represent " + length + "-bit array");
8494
}
8595

8696
this.length = length;
@@ -95,7 +105,7 @@ public BitArray(int length, byte[] a) throws IllegalArgumentException {
95105
2. zero out extra bits in the last byte
96106
*/
97107
repn = new byte[repLength];
98-
System.arraycopy(a, 0, repn, 0, repLength);
108+
System.arraycopy(a, ofs, repn, 0, repLength);
99109
if (repLength > 0) {
100110
repn[repLength - 1] &= bitMask;
101111
}
@@ -266,7 +276,7 @@ public String toString() {
266276
public BitArray truncate() {
267277
for (int i=length-1; i>=0; i--) {
268278
if (get(i)) {
269-
return new BitArray(i+1, Arrays.copyOf(repn, (i + BITS_PER_UNIT)/BITS_PER_UNIT));
279+
return new BitArray(i+1, repn, 0);
270280
}
271281
}
272282
return new BitArray(1);

src/java.base/share/classes/sun/security/util/DerValue.java

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,28 @@ public String getAsString() throws IOException {
692692
};
693693
}
694694

695+
// check the number of pad bits, validate the pad bits in the bytes
696+
// if enforcing DER (i.e. allowBER == false), and return the number of
697+
// bits of the resulting BitString
698+
private static int checkPaddedBits(int numOfPadBits, byte[] data,
699+
int start, int end, boolean allowBER) throws IOException {
700+
// number of pad bits should be from 0(min) to 7(max).
701+
if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
702+
throw new IOException("Invalid number of padding bits");
703+
}
704+
int lenInBits = ((end - start) << 3) - numOfPadBits;
705+
if (lenInBits < 0) {
706+
throw new IOException("Not enough bytes in BitString");
707+
}
708+
709+
// padding bits should be all zeros for DER
710+
if (!allowBER && numOfPadBits != 0 &&
711+
(data[end - 1] & (0xff >>> (8 - numOfPadBits))) != 0) {
712+
throw new IOException("Invalid value of padding bits");
713+
}
714+
return lenInBits;
715+
}
716+
695717
/**
696718
* Returns an ASN.1 BIT STRING value, with the tag assumed implicit
697719
* based on the parameter. The bit string must be byte-aligned.
@@ -708,18 +730,17 @@ public byte[] getBitString(boolean tagImplicit) throws IOException {
708730
}
709731
if (end == start) {
710732
throw new IOException("Invalid encoding: zero length bit string");
733+
711734
}
735+
data.pos = data.end; // Compatibility. Reach end.
736+
712737
int numOfPadBits = buffer[start];
713-
if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
714-
throw new IOException("Invalid number of padding bits");
715-
}
716-
// minus the first byte which indicates the number of padding bits
738+
checkPaddedBits(numOfPadBits, buffer, start + 1, end, allowBER);
717739
byte[] retval = Arrays.copyOfRange(buffer, start + 1, end);
718-
if (numOfPadBits != 0) {
719-
// get rid of the padding bits
720-
retval[end - start - 2] &= (byte)((0xff << numOfPadBits));
740+
if (allowBER && numOfPadBits != 0) {
741+
// fix the potential non-zero padding bits
742+
retval[retval.length - 1] &= (byte)((0xff << numOfPadBits));
721743
}
722-
data.pos = data.end; // Compatibility. Reach end.
723744
return retval;
724745
}
725746

@@ -742,16 +763,11 @@ public BitArray getUnalignedBitString(boolean tagImplicit)
742763
throw new IOException("Invalid encoding: zero length bit string");
743764
}
744765
data.pos = data.end; // Compatibility. Reach end.
766+
745767
int numOfPadBits = buffer[start];
746-
if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
747-
throw new IOException("Invalid number of padding bits");
748-
}
749-
if (end == start + 1) {
750-
return new BitArray(0);
751-
} else {
752-
return new BitArray(((end - start - 1) << 3) - numOfPadBits,
753-
Arrays.copyOfRange(buffer, start + 1, end));
754-
}
768+
int len = checkPaddedBits(numOfPadBits, buffer, start + 1, end,
769+
allowBER);
770+
return new BitArray(len, buffer, start + 1);
755771
}
756772

757773
/**
Lines changed: 58 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2002, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -26,52 +26,82 @@
2626
* @bug 4511556
2727
* @summary Verify BitString value containing padding bits is accepted.
2828
* @modules java.base/sun.security.util
29+
* @library /test/lib
2930
*/
30-
3131
import java.io.*;
32-
import java.util.Arrays;
3332
import java.math.BigInteger;
33+
import java.util.Arrays;
34+
import java.util.HexFormat;
35+
import jdk.test.lib.Asserts;
36+
import jdk.test.lib.Utils;
3437

38+
import sun.security.util.BitArray;
3539
import sun.security.util.DerInputStream;
3640

3741
public class PaddedBitString {
3842

3943
// Relaxed the BitString parsing routine to accept bit strings
40-
// with padding bits, ex. treat DER_BITSTRING_PAD6 as the same
41-
// bit string as DER_BITSTRING_NOPAD.
44+
// with padding bits, ex. treat DER_BITSTRING_PAD6_b as the same
45+
// bit string as DER_BITSTRING_PAD6_0/DER_BITSTRING_NOPAD.
4246
// Note:
4347
// 1. the number of padding bits has to be in [0...7]
4448
// 2. value of the padding bits is ignored
4549

46-
// bit string (01011101 11000000)
47-
// With 6 padding bits (01011101 11001011)
48-
private final static byte[] DER_BITSTRING_PAD6 = { 3, 3, 6,
49-
(byte)0x5d, (byte)0xcb };
50-
5150
// With no padding bits
5251
private final static byte[] DER_BITSTRING_NOPAD = { 3, 3, 0,
5352
(byte)0x5d, (byte)0xc0 };
53+
// With 6 zero padding bits (01011101 11000000)
54+
private final static byte[] DER_BITSTRING_PAD6_0 = { 3, 3, 6,
55+
(byte)0x5d, (byte)0xc0 };
5456

55-
public static void main(String args[]) throws Exception {
56-
byte[] ba0, ba1;
57-
try {
58-
DerInputStream derin = new DerInputStream(DER_BITSTRING_PAD6);
59-
ba1 = derin.getBitString();
60-
} catch( IOException e ) {
61-
e.printStackTrace();
62-
throw new Exception("Unable to parse BitString with 6 padding bits");
63-
}
57+
// With 6 nonzero padding bits (01011101 11001011)
58+
private final static byte[] DER_BITSTRING_PAD6_b = { 3, 3, 6,
59+
(byte)0x5d, (byte)0xcb };
6460

65-
try {
66-
DerInputStream derin = new DerInputStream(DER_BITSTRING_NOPAD);
67-
ba0 = derin.getBitString();
68-
} catch( IOException e ) {
69-
e.printStackTrace();
70-
throw new Exception("Unable to parse BitString with no padding");
71-
}
61+
// With 8 padding bits
62+
private final static byte[] DER_BITSTRING_PAD8_0 = { 3, 3, 8,
63+
(byte)0x5d, (byte)0xc0 };
64+
65+
private final static byte[] BITS = { (byte)0x5d, (byte)0xc0 };
66+
67+
static enum Type {
68+
BIT_STRING,
69+
UNALIGNED_BIT_STRING;
70+
}
71+
72+
public static void main(String args[]) throws Exception {
73+
test(DER_BITSTRING_NOPAD, new BitArray(16, BITS));
74+
test(DER_BITSTRING_PAD6_0, new BitArray(10, BITS));
75+
test(DER_BITSTRING_PAD6_b, new BitArray(10, BITS));
76+
test(DER_BITSTRING_PAD8_0, null);
77+
System.out.println("Tests Passed");
78+
}
7279

73-
if (Arrays.equals(ba1, ba0) == false ) {
74-
throw new Exception("BitString comparison check failed");
80+
private static void test(byte[] in, BitArray ans) throws IOException {
81+
System.out.println("Testing " +
82+
HexFormat.of().withUpperCase().formatHex(in));
83+
for (Type t : Type.values()) {
84+
DerInputStream derin = new DerInputStream(in);
85+
boolean shouldPass = (ans != null);
86+
switch (t) {
87+
case BIT_STRING:
88+
if (shouldPass) {
89+
Asserts.assertTrue(Arrays.equals(ans.toByteArray(),
90+
derin.getBitString()));
91+
} else {
92+
Utils.runAndCheckException(() -> derin.getBitString(),
93+
IOException.class);
94+
}
95+
break;
96+
case UNALIGNED_BIT_STRING:
97+
if (shouldPass) {
98+
Asserts.assertEQ(ans, derin.getUnalignedBitString());
99+
} else {
100+
Utils.runAndCheckException(() ->
101+
derin.getUnalignedBitString(), IOException.class);
102+
}
103+
break;
104+
}
75105
}
76106
}
77107
}

0 commit comments

Comments
 (0)