Skip to content

Commit 76322ec

Browse files
iigoninbennygoerzigKarstenSchnitterKai Sternad
committed
fix flaky test PemTrustConfigTests#testTrustConfigReloadsFileContents
Signed-off-by: Igonin <iigonin@sternad.de> Co-authored-by: Benny Goerzig <benny.goerzig@sap.com> Co-authored-by: Karsten Schnitter <k.schnitter@sap.com> Co-authored-by: Kai Sternad <k.sternad@sternad.de>
1 parent 292407b commit 76322ec

File tree

1 file changed

+44
-23
lines changed

1 file changed

+44
-23
lines changed

libs/ssl-config/src/test/java/org/opensearch/common/ssl/PemTrustConfigTests.java

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
import javax.net.ssl.X509ExtendedTrustManager;
3939

40+
import java.nio.charset.StandardCharsets;
4041
import java.nio.file.Files;
4142
import java.nio.file.NoSuchFileException;
4243
import java.nio.file.Path;
@@ -69,11 +70,29 @@ public void testBuildTrustConfigFromMultiplePemFiles() throws Exception {
6970
}
7071

7172
public void testBadFileFormatFails() throws Exception {
72-
final Path ca = createTempFile("ca", ".crt");
73-
Files.write(ca, generateRandomByteArrayOfLength(128), StandardOpenOption.APPEND);
74-
final PemTrustConfig trustConfig = new PemTrustConfig(Collections.singletonList(ca));
75-
assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ca));
76-
assertFailedToParse(trustConfig, ca);
73+
{
74+
final Path ca = createTempFile("ca", ".crt");
75+
Files.write(ca, "This is definitely not a PEM certificate".getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE);
76+
final PemTrustConfig trustConfig = new PemTrustConfig(Collections.singletonList(ca));
77+
assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ca));
78+
assertFailedToParse(trustConfig, ca);
79+
}
80+
81+
{
82+
final Path ca = createTempFile("ca", ".crt");
83+
Files.write(ca, generateInvalidPemBytes(), StandardOpenOption.CREATE);
84+
final PemTrustConfig trustConfig = new PemTrustConfig(Collections.singletonList(ca));
85+
assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ca));
86+
assertCannotCreateTrust(trustConfig, ca);
87+
}
88+
89+
{ // test DER-encoded sequence
90+
final Path ca = createTempFile("ca", ".crt");
91+
Files.write(ca, generateInvalidDerEncodedPemBytes(), StandardOpenOption.CREATE);
92+
final PemTrustConfig trustConfig = new PemTrustConfig(Collections.singletonList(ca));
93+
assertThat(trustConfig.getDependentFiles(), Matchers.containsInAnyOrder(ca));
94+
assertCannotCreateTrust(trustConfig, ca);
95+
}
7796
}
7897

7998
public void testEmptyFileFails() throws Exception {
@@ -119,8 +138,8 @@ public void testTrustConfigReloadsFileContents() throws Exception {
119138
Files.delete(ca1);
120139
assertFileNotFound(trustConfig, ca1);
121140

122-
Files.write(ca1, generateRandomByteArrayOfLength(128), StandardOpenOption.CREATE);
123-
assertFailedToParse(trustConfig, ca1);
141+
Files.write(ca1, generateInvalidPemBytes(), StandardOpenOption.CREATE);
142+
assertCannotCreateTrust(trustConfig, ca1);
124143
}
125144

126145
private void assertCertificateChain(PemTrustConfig trustConfig, String... caNames) {
@@ -134,13 +153,23 @@ private void assertCertificateChain(PemTrustConfig trustConfig, String... caName
134153
assertThat(issuerNames, Matchers.containsInAnyOrder(caNames));
135154
}
136155

156+
// The parser returns an empty collection when no valid sequence is found,
157+
// but our implementation requires an exception to be thrown in this case
137158
private void assertFailedToParse(PemTrustConfig trustConfig, Path file) {
138159
final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
139160
logger.info("failure", exception);
140161
assertThat(exception.getMessage(), Matchers.containsString(file.toAbsolutePath().toString()));
141162
assertThat(exception.getMessage(), Matchers.containsString("Failed to parse any certificate from"));
142163
}
143164

165+
// The parser encounters malformed PEM data
166+
private void assertCannotCreateTrust(PemTrustConfig trustConfig, Path file) {
167+
final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
168+
logger.info("failure", exception);
169+
assertThat(exception.getMessage(), Matchers.containsString(file.toAbsolutePath().toString()));
170+
assertThat(exception.getMessage(), Matchers.containsString("cannot create trust using PEM certificates"));
171+
}
172+
144173
private void assertFileNotFound(PemTrustConfig trustConfig, Path file) {
145174
final SslConfigException exception = expectThrows(SslConfigException.class, trustConfig::createTrustManager);
146175
assertThat(exception.getMessage(), Matchers.containsString("files do not exist"));
@@ -149,23 +178,15 @@ private void assertFileNotFound(PemTrustConfig trustConfig, Path file) {
149178
assertThat(exception.getCause(), Matchers.instanceOf(NoSuchFileException.class));
150179
}
151180

152-
private byte[] generateRandomByteArrayOfLength(int length) {
153-
byte[] bytes = randomByteArrayOfLength(length);
154-
/*
155-
* If the bytes represent DER encoded value indicating ASN.1 SEQUENCE followed by length byte if it is zero then while trying to
156-
* parse PKCS7 block from the encoded stream, it failed parsing the content type. The DerInputStream.getSequence() method in this
157-
* case returns an empty DerValue array but ContentType does not check the length of array before accessing the array resulting in a
158-
* ArrayIndexOutOfBoundsException. This check ensures that when we create random stream of bytes we do not create ASN.1 SEQUENCE
159-
* followed by zero length which fails the test intermittently.
160-
*/
161-
while (checkRandomGeneratedBytesRepresentZeroLengthDerSequenceCausingArrayIndexOutOfBound(bytes)) {
162-
bytes = randomByteArrayOfLength(length);
163-
}
164-
return bytes;
181+
private byte[] generateInvalidPemBytes() {
182+
String invalidPem = "-----BEGIN CERTIFICATE-----\nINVALID_CONTENT\n-----END CERTIFICATE-----";
183+
return invalidPem.getBytes(StandardCharsets.UTF_8);
165184
}
166185

167-
private static boolean checkRandomGeneratedBytesRepresentZeroLengthDerSequenceCausingArrayIndexOutOfBound(byte[] bytes) {
168-
// Tag value indicating an ASN.1 "SEQUENCE". Reference: sun.security.util.DerValue.tag_Sequence = 0x30
169-
return bytes[0] == 0x30 && bytes[1] == 0x00;
186+
private byte[] generateInvalidDerEncodedPemBytes() {
187+
byte[] shortFormZeroLength = { 0x30, 0x00 };
188+
byte[] longFormZeroLength = { 0x30, (byte) 0x81, 0x00 };
189+
byte[] indefiniteForm = { 0x30, (byte) 0x80, 0x01, 0x02, 0x00, 0x00 };
190+
return randomFrom(shortFormZeroLength, longFormZeroLength, indefiniteForm);
170191
}
171192
}

0 commit comments

Comments
 (0)