Skip to content

Commit

Permalink
write some additional tests.
Browse files Browse the repository at this point in the history
Signed-off-by: Iwan Igonin <iigonin@sternad.de>
  • Loading branch information
iigonin committed Oct 25, 2024
1 parent b30ebc0 commit 246aeed
Show file tree
Hide file tree
Showing 19 changed files with 406 additions and 80 deletions.
11 changes: 10 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,8 @@ gradle.projectsEvaluated {
}
}

// test retry configuration
subprojects {
// test retry configuration
tasks.withType(Test).configureEach {
develocity.testRetry {
if (BuildParams.isCi()) {
Expand Down Expand Up @@ -552,6 +552,15 @@ subprojects {
}
}
}

// test with FIPS-140-2 enabled
plugins.withType(JavaPlugin).configureEach {
tasks.withType(Test).configureEach { testTask ->
if (System.getenv('OPENSEARCH_CRYPTO_STANDARD') == 'FIPS-140-2') {
testTask.jvmArgs += "-Dorg.bouncycastle.fips.approved_only=true"
}
}
}
}

// eclipse configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,10 @@ public void execute(Task t) {
test.systemProperty("tests.seed", BuildParams.getTestSeed());
}

var securityFile = BuildParams.isInFipsJvm() ? "fips_java.security" : "java.security";
test.systemProperty(
"java.security.properties",
project.getRootProject().getLayout().getProjectDirectory() + "/distribution/src/config/fips_java.security"
project.getRootProject().getLayout().getProjectDirectory() + "/distribution/src/config/" + securityFile
);

// don't track these as inputs since they contain absolute paths and break cache relocatability
Expand Down
12 changes: 11 additions & 1 deletion distribution/src/config/fips_java.security
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
# Security Properties for JDK 11 and higher, with BouncyCastle FIPS provider and BouncyCastleJsseProvider in FIPS mode
# Security Properties for JDK 11 and higher, with BouncyCastle FIPS provider and BouncyCastleJsseProvider in approved-only mode

security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider C:HYBRID;ENABLE{All};
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider fips:BCFIPS
security.provider.3=SUN
security.provider.4=SunJGSS

securerandom.source=file:/dev/urandom
securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN
securerandom.drbg.config=

login.configuration.provider=sun.security.provider.ConfigFile
policy.provider=sun.security.provider.PolicyFile
policy.expandProperties=true
policy.allowSystemProperty=true
policy.ignoreIdentityScope=false
keystore.type.compat=true

package.access=sun.misc.,\
sun.reflect.
package.definition=sun.misc.,\
sun.reflect.

security.overridePropertiesFile=true

ssl.KeyManagerFactory.algorithm=PKIX
ssl.TrustManagerFactory.algorithm=PKIX

networkaddress.cache.negative.ttl=10
krb5.kdc.bad.policy = tryLast

jdk.certpath.disabledAlgorithms=MD2, MD5, SHA1, jdkCA&usageTLSServer, RSA keySize < 2048, DSA keySize < 2048, EC keySize < 224
jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 2048, DSA keySize < 2048
jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, MD5withRSA, DH keySize < 2048, EC keySize < 224, DES40_CBC, RC4_40, 3DES_EDE_CBC
Expand All @@ -31,7 +38,9 @@ jdk.tls.legacyAlgorithms= \
RC4_128, RC4_40, DES_CBC, DES40_CBC, \
3DES_EDE_CBC
jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37

crypto.policy=unlimited

jdk.xml.dsig.secureValidationPolicy=\
disallowAlg http://www.w3.org/TR/1999/REC-xslt-19991116,\
disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\
Expand All @@ -45,5 +54,6 @@ jdk.xml.dsig.secureValidationPolicy=\
minKeySize EC 224,\
noDuplicateIds,\
noRetrievalMethodLoops

jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep;\
java.base/java.security.KeyRep$Type;java.base/javax.crypto.spec.SecretKeySpec;!*
58 changes: 58 additions & 0 deletions distribution/src/config/java.security
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Security Properties for JDK 11 and higher, with BouncyCastle FIPS provider and BouncyCastleJSSEProvider in non-approved mode

security.provider.1=org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider
security.provider.2=org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
security.provider.3=SUN
security.provider.4=SunJGSS

securerandom.source=file:/dev/urandom
securerandom.strongAlgorithms=NativePRNGBlocking:SUN,DRBG:SUN
securerandom.drbg.config=

login.configuration.provider=sun.security.provider.ConfigFile
policy.provider=sun.security.provider.PolicyFile
policy.expandProperties=true
policy.allowSystemProperty=true
policy.ignoreIdentityScope=false
keystore.type.compat=true

package.access=sun.misc.,\
sun.reflect.
package.definition=sun.misc.,\
sun.reflect.

security.overridePropertiesFile=true

ssl.KeyManagerFactory.algorithm=PKIX
ssl.TrustManagerFactory.algorithm=PKIX

networkaddress.cache.negative.ttl=10
krb5.kdc.bad.policy = tryLast

jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, DSA keySize < 1024
jdk.jar.disabledAlgorithms=MD2, MD5, RSA keySize < 1024, DSA keySize < 1024
jdk.tls.disabledAlgorithms=SSLv3, RC4, MD5withRSA, DH keySize < 1024, DES40_CBC, RC4_40
jdk.tls.legacyAlgorithms= \
K_NULL, C_NULL, M_NULL, \
DH_anon, ECDH_anon, \
RC4_128, RC4_40, DES_CBC, DES40_CBC, \
3DES_EDE_CBC
jdk.tls.keyLimits=AES/GCM/NoPadding KeyUpdate 2^37

crypto.policy=unlimited

jdk.xml.dsig.secureValidationPolicy=\
disallowAlg http://www.w3.org/2001/04/xmldsig-more#rsa-md5,\
disallowAlg http://www.w3.org/2001/04/xmldsig-more#hmac-md5,\
disallowAlg http://www.w3.org/2001/04/xmldsig-more#md5,\
maxTransforms 5,\
maxReferences 30,\
disallowReferenceUriSchemes file http https,\
minKeySize RSA 1024,\
minKeySize DSA 1024,\
minKeySize EC 224,\
noDuplicateIds,\
noRetrievalMethodLoops

jceks.key.serialFilter = java.base/java.lang.Enum;java.base/java.security.KeyRep;\
java.base/java.security.KeyRep$Type;java.base/javax.crypto.spec.SecretKeySpec;!*
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,13 @@
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
Expand Down Expand Up @@ -363,31 +368,24 @@ public void testIllegalSettingName() throws Exception {
assertTrue(e.getMessage().contains("does not match the allowed setting name pattern"));
}

public void testBackcompatV1() throws Exception {
assumeFalse("Can't run in a FIPS JVM as PBE is not available", inFipsJvm());
Path configDir = env.configDir();
NIOFSDirectory directory = new NIOFSDirectory(configDir);
try (IndexOutput output = EndiannessReverserUtil.createOutput(directory, "opensearch.keystore", IOContext.DEFAULT)) {
CodecUtil.writeHeader(output, "opensearch.keystore", 1);
output.writeByte((byte) 0); // hasPassword = false
output.writeString("PKCS12");
output.writeString("PBE");
public void testFailLoadV1KeystoresInFipsJvm() throws Exception {
assumeTrue("Test in FIPS JVM", inFipsJvm());

SecretKeyFactory secretFactory = SecretKeyFactory.getInstance("PBE", "SunJCE");
KeyStore keystore = KeyStoreFactory.getInstance(KeyStoreType.PKCS_12, "SUN");
keystore.load(null, null);
SecretKey secretKey = secretFactory.generateSecret(new PBEKeySpec("stringSecretValue".toCharArray()));
KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(new char[0]);
keystore.setEntry("string_setting", new KeyStore.SecretKeyEntry(secretKey), protectionParameter);
Exception e = assertThrows(SecurityException.class, () -> generateV1());
assertThat(e.getMessage(), containsString("Only PKCS_11, BCFKS, JKS keystores are allowed in FIPS JVM"));
}

ByteArrayOutputStream keystoreBytesStream = new ByteArrayOutputStream();
keystore.store(keystoreBytesStream, new char[0]);
byte[] keystoreBytes = keystoreBytesStream.toByteArray();
output.writeInt(keystoreBytes.length);
output.writeBytes(keystoreBytes, keystoreBytes.length);
CodecUtil.writeFooter(output);
}
public void testFailLoadV2KeystoresInFipsJvm() throws Exception {
assumeTrue("Test in FIPS JVM", inFipsJvm());

Exception e = assertThrows(SecurityException.class, () -> generateV2());
assertThat(e.getMessage(), containsString("Only PKCS_11, BCFKS, JKS keystores are allowed in FIPS JVM"));
}

public void testBackcompatV1() throws Exception {
assumeFalse("Can't run in a FIPS JVM as PBE is not available", inFipsJvm());
generateV1();
Path configDir = env.configDir();
KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir);
keystore.decrypt(new char[0]);
SecureString testValue = keystore.getString("string_setting");
Expand All @@ -396,47 +394,8 @@ public void testBackcompatV1() throws Exception {

public void testBackcompatV2() throws Exception {
assumeFalse("Can't run in a FIPS JVM as PBE is not available", inFipsJvm());
byte[] fileBytes = generateV2();
Path configDir = env.configDir();
NIOFSDirectory directory = new NIOFSDirectory(configDir);
byte[] fileBytes = new byte[20];
random().nextBytes(fileBytes);
try (IndexOutput output = EndiannessReverserUtil.createOutput(directory, "opensearch.keystore", IOContext.DEFAULT)) {

CodecUtil.writeHeader(output, "opensearch.keystore", 2);
output.writeByte((byte) 0); // hasPassword = false
output.writeString("PKCS12");
output.writeString("PBE"); // string algo
output.writeString("PBE"); // file algo

output.writeVInt(2); // num settings
output.writeString("string_setting");
output.writeString("STRING");
output.writeString("file_setting");
output.writeString("FILE");

SecretKeyFactory secretFactory = SecretKeyFactory.getInstance("PBE", "SunJCE");
KeyStore keystore = KeyStoreFactory.getInstance(KeyStoreType.PKCS_12, "SUN");
keystore.load(null, null);
SecretKey secretKey = secretFactory.generateSecret(new PBEKeySpec("stringSecretValue".toCharArray()));
KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(new char[0]);
keystore.setEntry("string_setting", new KeyStore.SecretKeyEntry(secretKey), protectionParameter);

byte[] base64Bytes = Base64.getEncoder().encode(fileBytes);
char[] chars = new char[base64Bytes.length];
for (int i = 0; i < chars.length; ++i) {
chars[i] = (char) base64Bytes[i]; // PBE only stores the lower 8 bits, so this narrowing is ok
}
secretKey = secretFactory.generateSecret(new PBEKeySpec(chars));
keystore.setEntry("file_setting", new KeyStore.SecretKeyEntry(secretKey), protectionParameter);

ByteArrayOutputStream keystoreBytesStream = new ByteArrayOutputStream();
keystore.store(keystoreBytesStream, new char[0]);
byte[] keystoreBytes = keystoreBytesStream.toByteArray();
output.writeInt(keystoreBytes.length);
output.writeBytes(keystoreBytes, keystoreBytes.length);
CodecUtil.writeFooter(output);
}

KeyStoreWrapper keystore = KeyStoreWrapper.load(configDir);
keystore.decrypt(new char[0]);
SecureString testValue = keystore.getString("string_setting");
Expand Down Expand Up @@ -496,6 +455,77 @@ public void testLegacyV3() throws GeneralSecurityException, IOException {
assertThat(toByteArray(wrapper.getFile("file_setting")), equalTo("file_value".getBytes(StandardCharsets.UTF_8)));
}

private void generateV1() throws IOException, NoSuchAlgorithmException, NoSuchProviderException, CertificateException,
InvalidKeySpecException, KeyStoreException {
Path configDir = env.configDir();
NIOFSDirectory directory = new NIOFSDirectory(configDir);
try (IndexOutput output = EndiannessReverserUtil.createOutput(directory, "opensearch.keystore", IOContext.DEFAULT)) {
CodecUtil.writeHeader(output, "opensearch.keystore", 1);
output.writeByte((byte) 0); // hasPassword = false
output.writeString("PKCS12");
output.writeString("PBE");

SecretKeyFactory secretFactory = SecretKeyFactory.getInstance("PBE", "SunJCE");
KeyStore keystore = KeyStoreFactory.getInstance(KeyStoreType.PKCS_12, "SUN");
keystore.load(null, null);
SecretKey secretKey = secretFactory.generateSecret(new PBEKeySpec("stringSecretValue".toCharArray()));
KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(new char[0]);
keystore.setEntry("string_setting", new KeyStore.SecretKeyEntry(secretKey), protectionParameter);

ByteArrayOutputStream keystoreBytesStream = new ByteArrayOutputStream();
keystore.store(keystoreBytesStream, new char[0]);
byte[] keystoreBytes = keystoreBytesStream.toByteArray();
output.writeInt(keystoreBytes.length);
output.writeBytes(keystoreBytes, keystoreBytes.length);
CodecUtil.writeFooter(output);
}
}

private byte[] generateV2() throws Exception {
Path configDir = env.configDir();
NIOFSDirectory directory = new NIOFSDirectory(configDir);
byte[] fileBytes = new byte[20];
random().nextBytes(fileBytes);
try (IndexOutput output = EndiannessReverserUtil.createOutput(directory, "opensearch.keystore", IOContext.DEFAULT)) {

CodecUtil.writeHeader(output, "opensearch.keystore", 2);
output.writeByte((byte) 0); // hasPassword = false
output.writeString("PKCS12");
output.writeString("PBE"); // string algo
output.writeString("PBE"); // file algo

output.writeVInt(2); // num settings
output.writeString("string_setting");
output.writeString("STRING");
output.writeString("file_setting");
output.writeString("FILE");

SecretKeyFactory secretFactory = SecretKeyFactory.getInstance("PBE", "SunJCE");
KeyStore keystore = KeyStoreFactory.getInstance(KeyStoreType.PKCS_12, "SUN");
keystore.load(null, null);
SecretKey secretKey = secretFactory.generateSecret(new PBEKeySpec("stringSecretValue".toCharArray()));
KeyStore.ProtectionParameter protectionParameter = new KeyStore.PasswordProtection(new char[0]);
keystore.setEntry("string_setting", new KeyStore.SecretKeyEntry(secretKey), protectionParameter);

byte[] base64Bytes = Base64.getEncoder().encode(fileBytes);
char[] chars = new char[base64Bytes.length];
for (int i = 0; i < chars.length; ++i) {
chars[i] = (char) base64Bytes[i]; // PBE only stores the lower 8 bits, so this narrowing is ok
}
secretKey = secretFactory.generateSecret(new PBEKeySpec(chars));
keystore.setEntry("file_setting", new KeyStore.SecretKeyEntry(secretKey), protectionParameter);

ByteArrayOutputStream keystoreBytesStream = new ByteArrayOutputStream();
keystore.store(keystoreBytesStream, new char[0]);
byte[] keystoreBytes = keystoreBytesStream.toByteArray();
output.writeInt(keystoreBytes.length);
output.writeBytes(keystoreBytes, keystoreBytes.length);
CodecUtil.writeFooter(output);
}

return fileBytes;
}

private byte[] toByteArray(final InputStream is) throws IOException {
final ByteArrayOutputStream os = new ByteArrayOutputStream();
final byte[] buffer = new byte[1024];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@

final class SystemJvmOptions {

protected static final String OPENSEARCH_CRYPTO_STANDARD = "OPENSEARCH_CRYPTO_STANDARD";
protected static final String FIPS_140_2 = "FIPS-140-2";

static List<String> systemJvmOptions(final Path config) {
return Collections.unmodifiableList(
Arrays.asList(
Expand Down Expand Up @@ -88,16 +91,22 @@ static List<String> systemJvmOptions(final Path config) {
}

private static String enableFips() {
var cryptoStandard = System.getenv("OPENSEARCH_CRYPTO_STANDARD");
if (cryptoStandard != null && cryptoStandard.equals("FIPS-140-2")) {
var cryptoStandard = System.getenv(OPENSEARCH_CRYPTO_STANDARD);
if (cryptoStandard != null && cryptoStandard.equals(FIPS_140_2)) {
return "-Dorg.bouncycastle.fips.approved_only=true";
}
return "";
}

private static String loadJavaSecurityProperties(final Path config) {
var securityFile = config.resolve("fips_java.security");
return "-Djava.security.properties=" + securityFile.toAbsolutePath();
String securityFile;
var cryptoStandard = System.getenv(OPENSEARCH_CRYPTO_STANDARD);
if (cryptoStandard != null && cryptoStandard.equals(FIPS_140_2)) {
securityFile = "fips_java.security";
} else {
securityFile = "java.security";
}
return "-Djava.security.properties=" + config.resolve(securityFile).toAbsolutePath();
}

private static String allowSecurityManagerOption() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,13 @@ public enum KeyStoreType {
JKS("JKS"),
PKCS_12("PKCS12"),
PKCS_11("PKCS11"),
BKS("BKS"),
BCFKS("BCFKS");

private static final Map<KeyStoreType, List<String>> TYPE_TO_EXTENSION_MAP = new HashMap<>();
static final Map<KeyStoreType, List<String>> TYPE_TO_EXTENSION_MAP = new HashMap<>();

static {
TYPE_TO_EXTENSION_MAP.put(JKS, List.of(".jks", ".ks"));
TYPE_TO_EXTENSION_MAP.put(PKCS_12, List.of(".p12", ".pkcs12", ".pfx"));
TYPE_TO_EXTENSION_MAP.put(BKS, List.of(".bks")); // Bouncy Castle Keystore
TYPE_TO_EXTENSION_MAP.put(BCFKS, List.of(".bcfks")); // Bouncy Castle FIPS Keystore
}

Expand Down
Loading

0 comments on commit 246aeed

Please sign in to comment.