Skip to content

Commit

Permalink
don't use endianness reverse util for writing KeyStore file (#78304)
Browse files Browse the repository at this point in the history
The different endianness of different versions is handled when reading files.
  • Loading branch information
iverase authored Sep 27, 2021
1 parent 421b3e8 commit b75b030
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import org.apache.lucene.backward_codecs.store.EndiannessReverserUtil;
import org.apache.lucene.codecs.CodecUtil;
import org.apache.lucene.store.DataOutput;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.IOContext;
import org.apache.lucene.store.IndexOutput;
Expand Down Expand Up @@ -204,9 +203,9 @@ public void testFailWhenCannotConsumeSecretStream() throws Exception {
Path configDir = env.configFile();
try (
Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = directory.createOutput("elasticsearch.keystore", IOContext.DEFAULT)
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
) {
CodecUtil.writeHeader(indexOutput, "elasticsearch.keystore", 3);
CodecUtil.writeHeader(indexOutput, "elasticsearch.keystore", KeyStoreWrapper.V3_VERSION);
indexOutput.writeByte((byte) 0); // No password
SecureRandom random = Randomness.createSecure();
byte[] salt = new byte[64];
Expand Down Expand Up @@ -235,19 +234,19 @@ public void testFailWhenCannotConsumeEncryptedBytesStream() throws Exception {
Path configDir = env.configFile();
try (
Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = directory.createOutput("elasticsearch.keystore", IOContext.DEFAULT)
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
) {
CodecUtil.writeHeader(indexOutput, "elasticsearch.keystore", 3);
CodecUtil.writeHeader(indexOutput, "elasticsearch.keystore", KeyStoreWrapper.V3_VERSION);
indexOutput.writeByte((byte) 0); // No password
SecureRandom random = Randomness.createSecure();
byte[] salt = new byte[64];
random.nextBytes(salt);
byte[] iv = new byte[12];
random.nextBytes(iv);

ByteArrayOutputStream bytes = new ByteArrayOutputStream();
CipherOutputStream cipherStream = getCipherStream(bytes, salt, iv);
DataOutputStream output = new DataOutputStream(cipherStream);

possiblyAlterSecretString(output, 0);
cipherStream.close();
final byte[] encryptedBytes = bytes.toByteArray();
Expand All @@ -259,17 +258,17 @@ public void testFailWhenCannotConsumeEncryptedBytesStream() throws Exception {
KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir);
SecurityException e = expectThrows(SecurityException.class, () -> keystore.decrypt(new char[0]));
assertThat(e.getMessage(), containsString("Keystore has been corrupted or tampered with"));
assertThat(e.getCause(), instanceOf(EOFException.class));
assertThat(e.getCause(), instanceOf(ArrayIndexOutOfBoundsException.class));
}

public void testFailWhenSecretStreamNotConsumed() throws Exception {
assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm());
Path configDir = env.configFile();
try (
Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = directory.createOutput("elasticsearch.keystore", IOContext.DEFAULT)
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
) {
CodecUtil.writeHeader(indexOutput, "elasticsearch.keystore", 3);
CodecUtil.writeHeader(indexOutput, "elasticsearch.keystore", KeyStoreWrapper.V3_VERSION);
indexOutput.writeByte((byte) 0); // No password
SecureRandom random = Randomness.createSecure();
byte[] salt = new byte[64];
Expand Down Expand Up @@ -297,9 +296,9 @@ public void testFailWhenEncryptedBytesStreamIsNotConsumed() throws Exception {
Path configDir = env.configFile();
try (
Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = directory.createOutput("elasticsearch.keystore", IOContext.DEFAULT)
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
) {
CodecUtil.writeHeader(indexOutput, "elasticsearch.keystore", 3);
CodecUtil.writeHeader(indexOutput, "elasticsearch.keystore", KeyStoreWrapper.V3_VERSION);
indexOutput.writeByte((byte) 0); // No password
SecureRandom random = Randomness.createSecure();
byte[] salt = new byte[64];
Expand Down Expand Up @@ -342,14 +341,8 @@ private void possiblyAlterSecretString(DataOutputStream output, int truncLength)
output.write(secret_value);
}

private void possiblyAlterEncryptedBytes(
IndexOutput indexOutput,
byte[] salt,
byte[] iv,
byte[] encryptedBytes,
int truncEncryptedDataLength
) throws Exception {
DataOutput out = EndiannessReverserUtil.wrapDataOutput(indexOutput);
private void possiblyAlterEncryptedBytes(IndexOutput out, byte[] salt, byte[] iv, byte[] encryptedBytes, int truncEncryptedDataLength)
throws Exception {
out.writeInt(4 + salt.length + 4 + iv.length + 4 + encryptedBytes.length);
out.writeInt(salt.length);
out.writeBytes(salt, salt.length);
Expand Down Expand Up @@ -424,7 +417,7 @@ public void testBackcompatV2() throws Exception {
Directory directory = newFSDirectory(configDir);
IndexOutput output = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT);
) {
CodecUtil.writeHeader(output, "elasticsearch.keystore", 2);
CodecUtil.writeHeader(output, "elasticsearch.keystore", KeyStoreWrapper.V2_VERSION);
output.writeByte((byte) 0); // hasPassword = false
output.writeString("PKCS12");
output.writeString("PBE"); // string algo
Expand Down Expand Up @@ -474,6 +467,42 @@ public void testBackcompatV2() throws Exception {
}
}

public void testBackcompatV4() throws Exception {
assumeFalse("Can't run in a FIPS JVM as PBE is not available", inFipsJvm());
Path configDir = env.configFile();
try (
Directory directory = newFSDirectory(configDir);
IndexOutput indexOutput = EndiannessReverserUtil.createOutput(directory, "elasticsearch.keystore", IOContext.DEFAULT)
) {
CodecUtil.writeHeader(indexOutput, "elasticsearch.keystore", KeyStoreWrapper.V4_VERSION);
indexOutput.writeByte((byte) 0); // No password
SecureRandom random = Randomness.createSecure();
byte[] salt = new byte[64];
random.nextBytes(salt);
byte[] iv = new byte[12];
random.nextBytes(iv);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
CipherOutputStream cipherStream = getCipherStream(bytes, salt, iv);
DataOutputStream output = new DataOutputStream(cipherStream);
{
byte[] secret_value = "super_secret_value".getBytes(StandardCharsets.UTF_8);
output.writeInt(1); // One entry
output.writeUTF("string_setting");
output.writeInt(secret_value.length);
output.write(secret_value);
}
cipherStream.close();
final byte[] encryptedBytes = bytes.toByteArray();
possiblyAlterEncryptedBytes(indexOutput, salt, iv, encryptedBytes, 0);
CodecUtil.writeFooter(indexOutput);
}

KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir);
keystore.decrypt(new char[0]);
SecureString testValue = keystore.getString("string_setting");
assertThat(testValue.toString(), equalTo("super_secret_value"));
}

public void testStringAndFileDistinction() throws Exception {
final char[] password = getPossibleKeystorePassword();
final KeyStoreWrapper wrapper = KeyStoreWrapper.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,28 @@ protected Environment createEnv(final Map<String, String> settings) {
};
}

public void testKeystoreUpgrade() throws Exception {
public void testKeystoreUpgradeV3() throws Exception {
assertKeystoreUpgrade("/format-v3-elasticsearch.keystore", KeyStoreWrapper.V3_VERSION);
}

public void testKeystoreUpgradeV4() throws Exception {
assertKeystoreUpgrade("/format-v4-elasticsearch.keystore", KeyStoreWrapper.V4_VERSION);
}

private void assertKeystoreUpgrade(String file, int version) throws Exception {
assumeFalse("Cannot open unprotected keystore on FIPS JVM", inFipsJvm());
final Path keystore = KeyStoreWrapper.keystorePath(env.configFile());
try (
InputStream is = KeyStoreWrapperTests.class.getResourceAsStream("/format-v3-elasticsearch.keystore");
OutputStream os = Files.newOutputStream(keystore)
) {
try (InputStream is = KeyStoreWrapperTests.class.getResourceAsStream(file); OutputStream os = Files.newOutputStream(keystore)) {
is.transferTo(os);
}
try (KeyStoreWrapper beforeUpgrade = KeyStoreWrapper.load(env.configFile())) {
assertNotNull(beforeUpgrade);
assertThat(beforeUpgrade.getFormatVersion(), equalTo(3));
assertThat(beforeUpgrade.getFormatVersion(), equalTo(version));
}
execute();
try (KeyStoreWrapper afterUpgrade = KeyStoreWrapper.load(env.configFile())) {
assertNotNull(afterUpgrade);
assertThat(afterUpgrade.getFormatVersion(), equalTo(KeyStoreWrapper.FORMAT_VERSION));
assertThat(afterUpgrade.getFormatVersion(), equalTo(KeyStoreWrapper.CURRENT_VERSION));
afterUpgrade.decrypt(new char[0]);
assertThat(afterUpgrade.getSettingNames(), hasItem(KeyStoreWrapper.SEED_SETTING.getKey()));
}
Expand Down
Binary file not shown.
Loading

0 comments on commit b75b030

Please sign in to comment.