diff --git a/CMakeLists.txt b/CMakeLists.txt index b7c43964..fadc0ff7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,9 +41,15 @@ set(ENABLE_NATIVE_TEST_HOOKS NO CACHE BOOL "Enable debugging hooks in the RNG. D set(TEST_DATA_DIR ${PROJECT_SOURCE_DIR}/test-data/ CACHE STRING "Path to directory containing test data") set(ORIG_SRCROOT ${PROJECT_SOURCE_DIR} CACHE STRING "Path to root of original package") set(PROVIDER_VERSION_STRING "" CACHE STRING "X.Y.Z formatted version of the provider") +set(EXPERIMENTAL_FIPS NO CACHE BOOL "Determines if this build is for FIPS mode with extra features from a non-FIPS branch of AWS-LC.") set(FIPS NO CACHE BOOL "Determine if this build is for FIPS mode") set(ALWAYS_ALLOW_EXTERNAL_LIB NO CACHE BOOL "Always permit tests to load ACCP shared objects from the library path") +if (EXPERIMENTAL_FIPS) + set(FIPS ON) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEXPERIMENTAL_FIPS_BUILD") +endif() + if (USE_CLANG_TIDY) # https://releases.llvm.org/9.0.0/tools/clang/tools/extra/docs/clang-tidy/checks/list.html # https://clang.llvm.org/extra/clang-tidy/#suppressing-undesired-diagnostics @@ -284,6 +290,11 @@ if(FIPS) set(TEST_FIPS_PROPERTY "-DFIPS=true") else() set(TEST_FIPS_PROPERTY "-DFIPS=false") +endif() + +# The source files under this guard should be removed and added to all builds, including FIPS, +# once the corresponding algorithms are added to a FIPS branch of AWS-LC consumable by ACCP. +if(EXPERIMENTAL_FIPS OR (NOT FIPS)) set(C_SRC ${C_SRC} csrc/concatenation_kdf.cpp csrc/counter_kdf.cpp) @@ -303,10 +314,8 @@ add_custom_command( add_custom_target(accp-jar-source DEPENDS ${ACCP_JAR_SOURCE}) if(ENABLE_NATIVE_TEST_HOOKS) - add_executable(test_keyutils EXCLUDE_FROM_ALL - csrc/test_keyutils.cpp - ) - link_with_openssl(test_keyutils) + add_executable(test_keyutils EXCLUDE_FROM_ALL csrc/test_keyutils.cpp) + # No need to link OpenSSL (AWS-LC) target_link_libraries(test_keyutils amazonCorrettoCryptoProvider) endif() @@ -875,4 +884,4 @@ add_custom_target(check-integration set_target_properties(check-integration PROPERTIES EXCLUDE_FROM_ALL 1) # Do this at the end, after we finish all our feature tests, or it'll be missing flags -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/csrc/config.h.in ${JNI_HEADER_DIR}/config.h) \ No newline at end of file +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/csrc/config.h.in ${JNI_HEADER_DIR}/config.h) diff --git a/README.md b/README.md index 1867d32c..a0cabee5 100644 --- a/README.md +++ b/README.md @@ -267,6 +267,10 @@ The FIPS builds use a different version of AWS-LC along with `FIPS=1` build flag AWS-LC will have FIPS certification. As a result, ACCP in FIPS mode only uses a version of AWS-LC that has FIPS certification or it will have in future. +By providing `-DEXPERIMENTAL_FIPS=true` to `gradlew` you will cause the entire build to be for a "FIPS mode" +build, and it uses the same version of AWS-LC as non-FIPS builds. This allows one to experiment with APIs +and features in AWS-LC that have not yet made it into a FIPS branch/release of AWS-LC, but built in FIPS mode. + When changing between FIPS and non-FIPS builds, be sure to do a full `clean` of your build environment. ##### All targets diff --git a/aws-lc b/aws-lc index 05747780..2f187975 160000 --- a/aws-lc +++ b/aws-lc @@ -1 +1 @@ -Subproject commit 05747780676652f41d0b9c570a495e4bb6608560 +Subproject commit 2f1879759b2e0fc70592665bdf10087b64f44b7d diff --git a/build.gradle b/build.gradle index c4c82e18..674af55b 100644 --- a/build.gradle +++ b/build.gradle @@ -14,11 +14,18 @@ plugins { group = 'software.amazon.cryptools' version = '2.5.0' -ext.isFips = Boolean.getBoolean('FIPS') -if (ext.isFips) { - ext.awsLcGitVersionId = 'AWS-LC-FIPS-2.0.13' +ext.isExperimentalFips = Boolean.getBoolean('EXPERIMENTAL_FIPS') +if (ext.isExperimentalFips) { + ext.isFips = true } else { - ext.awsLcGitVersionId = 'v1.33.0' + ext.isFips = Boolean.getBoolean('FIPS') +} + +if (ext.isExperimentalFips || !ext.isFips) { + // Experimental FIPS uses the same AWS-LC version as non-FIPS builds. + ext.awsLcGitVersionId = 'v1.34.2' +} else { + ext.awsLcGitVersionId = 'AWS-LC-FIPS-2.0.15' } // Check for user inputted git version ID. @@ -239,9 +246,11 @@ task buildAwsLc { args "-DCMAKE_INSTALL_PREFIX=${sharedObjectOutDir}" args "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON" + if (isFips) { args '-DFIPS=1' } + args '.' } } @@ -324,6 +333,9 @@ task executeCmake(type: Exec) { if (isFips) { args "-DFIPS=ON" } + if (isExperimentalFips) { + args '-DEXPERIMENTAL_FIPS=ON' + } if (prebuiltJar != null) { args '-DSIGNED_JAR=' + prebuiltJar @@ -519,6 +531,9 @@ task coverage_cmake(type: Exec) { if (isFips) { args "-DFIPS=ON" } + if (isExperimentalFips) { + args '-DEXPERIMENTAL_FIPS=ON' + } if (System.properties['JAVA_HOME'] != null) { args '-DJAVA_HOME=' + System.properties['JAVA_HOME'] diff --git a/csrc/keyutils.cpp b/csrc/keyutils.cpp index c6948323..850a473f 100644 --- a/csrc/keyutils.cpp +++ b/csrc/keyutils.cpp @@ -170,7 +170,7 @@ const EVP_MD* digestFromJstring(raii_env& env, jstring digestName) RSA* new_private_RSA_key_with_no_e(BIGNUM const* n, BIGNUM const* d) { -#ifdef FIPS_BUILD +#if defined FIPS_BUILD && !defined EXPERIMENTAL_FIPS_BUILD // AWS-LC-FIPS doesn't have RSA_new_private_key_no_e method yet. // The following implementation has been copied from AWS-LC: // https://github.com/aws/aws-lc/blob/v1.30.1/crypto/fipsmodule/rsa/rsa.c#L147 diff --git a/csrc/loader.cpp b/csrc/loader.cpp index 493a759a..34bd2185 100644 --- a/csrc/loader.cpp +++ b/csrc/loader.cpp @@ -47,6 +47,15 @@ JNIEXPORT jboolean JNICALL Java_com_amazon_corretto_crypto_provider_Loader_isFip return FIPS_mode() == 1 ? JNI_TRUE : JNI_FALSE; } +JNIEXPORT jboolean JNICALL Java_com_amazon_corretto_crypto_provider_Loader_isExperimentalFipsMode(JNIEnv*, jclass) +{ +#ifdef EXPERIMENTAL_FIPS_BUILD + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + JNIEXPORT jstring JNICALL Java_com_amazon_corretto_crypto_provider_Loader_getNativeLibraryVersion(JNIEnv* pEnv, jclass) { try { diff --git a/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java b/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java index 5e096d97..a3259670 100644 --- a/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java +++ b/src/com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.java @@ -104,7 +104,7 @@ private void buildServiceMap() { addService("SecretKeyFactory", HKDF_WITH_SHA512, hkdfSpi, false); // Once these KDFs are added to a FIPS branch of AWS-LC, we can remove this check. - if (!Loader.FIPS_BUILD) { + if (!Loader.FIPS_BUILD || Loader.EXPERIMENTAL_FIPS_BUILD) { final String concatenationKdfSpi = "ConcatenationKdfSpi"; addService("SecretKeyFactory", CKDF_WITH_SHA256, concatenationKdfSpi, false); addService("SecretKeyFactory", CKDF_WITH_SHA384, concatenationKdfSpi, false); @@ -628,6 +628,17 @@ public boolean isFips() { return Loader.FIPS_BUILD; } + /** + * ACCP-FIPS uses the FIPS branches/releases of AWS-LC. Experimental FIPS mode is to allow + * building ACCP and AWS-LC in FIPS mode using non-FIPS branches/release. This allows one to + * experiment with features that are not in FIPS branches yet. + * + *
Returns {@code true} if and only if the underlying ACCP is built in experimental fips mode. + */ + public boolean isExperimentalFips() { + return Loader.EXPERIMENTAL_FIPS_BUILD; + } + /** * Register ACCP's EC-flavored AlgorithmParameters implementation * diff --git a/src/com/amazon/corretto/crypto/provider/Loader.java b/src/com/amazon/corretto/crypto/provider/Loader.java index de3606d0..413f9e16 100644 --- a/src/com/amazon/corretto/crypto/provider/Loader.java +++ b/src/com/amazon/corretto/crypto/provider/Loader.java @@ -76,6 +76,8 @@ final class Loader { /** Indicates that libcrypto reports we are in a FIPS mode. */ static final boolean FIPS_BUILD; + static final boolean EXPERIMENTAL_FIPS_BUILD; + /** * Returns an InputStream associated with {@code fileName} contained in the "testdata" * subdirectory, relative to the location of this class file within the jar/jmod. @@ -158,6 +160,7 @@ static String getProperty(String propertyName, String def) { PROVIDER_VERSION_STR = versionStr; PROVIDER_VERSION = oldVersion; FIPS_BUILD = available && isFipsMode(); + EXPERIMENTAL_FIPS_BUILD = available && isExperimentalFipsMode(); // Check for native/java library version mismatch if (available) { @@ -332,6 +335,8 @@ static void checkNativeLibraryAvailability() { */ private static native boolean isFipsMode(); + private static native boolean isExperimentalFipsMode(); + /** Throws an {@link AssertionError} if the java and native libraries do not match versions. */ private static void assertVersionMatch() { final String nativeVersion; diff --git a/tests/ci/run_accp_basic_tests.sh b/tests/ci/run_accp_basic_tests.sh index d66322f4..fa810f3f 100755 --- a/tests/ci/run_accp_basic_tests.sh +++ b/tests/ci/run_accp_basic_tests.sh @@ -3,6 +3,8 @@ set -exo pipefail # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 +testing_experimental_fips=false + # Testing non-FIPS is the default. testing_fips=false # Depending on lcov version, either inconsistent or source needs to be passed @@ -18,6 +20,11 @@ while [[ $# -gt 0 ]]; do lcov_ignore="$2" shift 2 ;; + --experimental-fips) + testing_experimental_fips=true + shift + ;; + *) echo "$1 is not supported." exit 1 @@ -31,7 +38,7 @@ version=$($TEST_JAVA_HOME/bin/java -version 2>&1 | head -1 | cut -d'"' -f2 | sed # The JDK version should be least 10 for a regular ACCP build. We can # still test on older versions with the TEST_JAVA_HOME property. if (( "$version" <= "10" )); then - ./gradlew -DTEST_JAVA_HOME=$TEST_JAVA_HOME -DTEST_JAVA_MAJOR_VERSION=$version -DFIPS=$testing_fips -DLCOV_IGNORE=$lcov_ignore coverage test + ./gradlew -DTEST_JAVA_HOME=$TEST_JAVA_HOME -DTEST_JAVA_MAJOR_VERSION=$version -DEXPERIMENTAL_FIPS=$testing_experimental_fips -DFIPS=$testing_fips -DLCOV_IGNORE=$lcov_ignore coverage test exit $? fi @@ -41,4 +48,4 @@ fi export JAVA_HOME=$TEST_JAVA_HOME export PATH=$JAVA_HOME/bin:$PATH -./gradlew -DFIPS=$testing_fips -DLCOV_IGNORE=$lcov_ignore release +./gradlew -DEXPERIMENTAL_FIPS=$testing_experimental_fips -DFIPS=$testing_fips -DLCOV_IGNORE=$lcov_ignore release diff --git a/tests/ci/run_accp_test_integration.sh b/tests/ci/run_accp_test_integration.sh index c7eb3313..1c337050 100755 --- a/tests/ci/run_accp_test_integration.sh +++ b/tests/ci/run_accp_test_integration.sh @@ -3,6 +3,8 @@ set -exo pipefail # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 +testing_experimental_fips=false + # Testing non-FIPS is the default. testing_fips=false while [[ $# -gt 0 ]]; do @@ -10,6 +12,9 @@ while [[ $# -gt 0 ]]; do --fips) testing_fips=true ;; + --experimental-fips) + testing_experimental_fips=true + ;; *) echo "${1} is not supported." exit 1 @@ -25,7 +30,7 @@ version=$($TEST_JAVA_HOME/bin/java -version 2>&1 | head -1 | cut -d'"' -f2 | sed # The JDK version should be least 10 for a regular ACCP build. We can # still test on older versions with the TEST_JAVA_HOME property. if (( "$version" <= "10" )); then - ./gradlew -DTEST_JAVA_HOME=$TEST_JAVA_HOME -DTEST_JAVA_MAJOR_VERSION=$version -DFIPS=$testing_fips test_integration + ./gradlew -DTEST_JAVA_HOME=$TEST_JAVA_HOME -DTEST_JAVA_MAJOR_VERSION=$version -DEXPERIMENTAL_FIPS=$testing_experimental_fips -DFIPS=$testing_fips test_integration exit $? fi @@ -35,4 +40,4 @@ fi export JAVA_HOME=$TEST_JAVA_HOME export PATH=$JAVA_HOME/bin:$PATH -./gradlew -DFIPS=$testing_fips test_integration +./gradlew -DEXPERIMENTAL_FIPS=$testing_experimental_fips -DFIPS=$testing_fips test_integration diff --git a/tst/com/amazon/corretto/crypto/provider/test/ConcatenationKdfTest.java b/tst/com/amazon/corretto/crypto/provider/test/ConcatenationKdfTest.java index c4b15f03..f1f574d4 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/ConcatenationKdfTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/ConcatenationKdfTest.java @@ -6,10 +6,12 @@ import static com.amazon.corretto.crypto.provider.test.TestUtil.getEntriesFromFile; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeFalse; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import com.amazon.corretto.crypto.provider.ConcatenationKdfSpec; import java.security.NoSuchAlgorithmException; @@ -45,8 +47,9 @@ public void concatenationKdfsAreNotAvailableInFipsMode() { alg -> { try { assertNotNull(SecretKeyFactory.getInstance(alg, TestUtil.NATIVE_PROVIDER)); + assertTrue(TestUtil.supportsExtraKdfs()); } catch (final NoSuchAlgorithmException e) { - assertTrue(TestUtil.isFips()); + assertFalse(TestUtil.supportsExtraKdfs()); } }); } @@ -65,10 +68,10 @@ public void outputLengthCannotBeZeroOrNegative() { IllegalArgumentException.class, () -> new ConcatenationKdfSpec(new byte[10], -1, "name")); } - // The rest of the tests are only available in non-FIPS mode. + // The rest of the tests are only available in non-FIPS mode, or in experimental FIPS mode. @Test public void concatenationKdfExpectsConcatenationKdfSpecAsKeySpec() throws Exception { - assumeFalse(TestUtil.isFips()); + assumeTrue(TestUtil.supportsExtraKdfs()); final SecretKeyFactory skf = SecretKeyFactory.getInstance("ConcatenationKdfWithSha256", TestUtil.NATIVE_PROVIDER); assertThrows( @@ -77,7 +80,7 @@ public void concatenationKdfExpectsConcatenationKdfSpecAsKeySpec() throws Except @Test public void concatenationKdfWithEmptyInfoIsFine() throws Exception { - assumeFalse(TestUtil.isFips()); + assumeTrue(TestUtil.supportsExtraKdfs()); final SecretKeyFactory skf = SecretKeyFactory.getInstance("ConcatenationKdfWithSha256", TestUtil.NATIVE_PROVIDER); final ConcatenationKdfSpec spec = new ConcatenationKdfSpec(new byte[1], 10, "name"); @@ -87,7 +90,7 @@ public void concatenationKdfWithEmptyInfoIsFine() throws Exception { @Test public void concatenationKdfHmacWithEmptySaltIsFine() throws Exception { - assumeFalse(TestUtil.isFips()); + assumeTrue(TestUtil.supportsExtraKdfs()); final SecretKeyFactory skf = SecretKeyFactory.getInstance("ConcatenationKdfWithHmacSha256", TestUtil.NATIVE_PROVIDER); final ConcatenationKdfSpec spec1 = new ConcatenationKdfSpec(new byte[1], 10, "name"); @@ -105,7 +108,7 @@ public void concatenationKdfHmacWithEmptySaltIsFine() throws Exception { @ParameterizedTest(name = "{0}") @MethodSource("sskdfKatTests") public void concatenationKdfKatTests(final RspTestEntry entry) throws Exception { - assumeFalse(TestUtil.isFips()); + assumeTrue(TestUtil.supportsExtraKdfs()); final String digest = jceDigestName(entry.getInstance("HASH")); assumeFalse("SHA1".equals(digest) || "SHA224".equals(digest)); final boolean digestPrf = entry.getInstance("VARIANT").equals("DIGEST"); diff --git a/tst/com/amazon/corretto/crypto/provider/test/CounterKdfTest.java b/tst/com/amazon/corretto/crypto/provider/test/CounterKdfTest.java index 9a415773..b8382718 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/CounterKdfTest.java +++ b/tst/com/amazon/corretto/crypto/provider/test/CounterKdfTest.java @@ -6,10 +6,12 @@ import static com.amazon.corretto.crypto.provider.test.TestUtil.getEntriesFromFile; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeFalse; +import static org.junit.jupiter.api.Assumptions.assumeTrue; import com.amazon.corretto.crypto.provider.CounterKdfSpec; import java.security.NoSuchAlgorithmException; @@ -40,8 +42,9 @@ public void counterKdfsAreNotAvailableInFipsMode() { alg -> { try { assertNotNull(SecretKeyFactory.getInstance(alg, TestUtil.NATIVE_PROVIDER)); + assertTrue(TestUtil.supportsExtraKdfs()); } catch (final NoSuchAlgorithmException e) { - assertTrue(TestUtil.isFips()); + assertFalse(TestUtil.supportsExtraKdfs()); } }); } @@ -57,10 +60,10 @@ public void outputLengthCannotBeZeroOrNegative() { assertThrows(IllegalArgumentException.class, () -> new CounterKdfSpec(new byte[1], -1, "name")); } - // The rest of the tests are only available in non-FIPS mode. + // The rest of the tests are only available in non-FIPS mode, or in experimental FIPS mode. @Test public void counterKdfExpectsCounterKdfSpecAsKeySpec() throws Exception { - assumeFalse(TestUtil.isFips()); + assumeTrue(TestUtil.supportsExtraKdfs()); final SecretKeyFactory skf = SecretKeyFactory.getInstance("CounterKdfWithHmacSHA256", TestUtil.NATIVE_PROVIDER); assertThrows( @@ -69,7 +72,7 @@ public void counterKdfExpectsCounterKdfSpecAsKeySpec() throws Exception { @Test public void counterKdfWithEmptyInfoIsFine() throws Exception { - assumeFalse(TestUtil.isFips()); + assumeTrue(TestUtil.supportsExtraKdfs()); final SecretKeyFactory skf = SecretKeyFactory.getInstance("CounterKdfWithHmacSHA256", TestUtil.NATIVE_PROVIDER); final CounterKdfSpec spec = new CounterKdfSpec(new byte[1], 10, "name"); @@ -80,7 +83,7 @@ public void counterKdfWithEmptyInfoIsFine() throws Exception { @ParameterizedTest(name = "{0}") @MethodSource("counterKdfKatTests") public void counterKdfKatTests(final RspTestEntry entry) throws Exception { - assumeFalse(TestUtil.isFips()); + assumeTrue(TestUtil.supportsExtraKdfs()); final String digest = jceDigestName(entry.getInstance("HASH")); assumeFalse("SHA1".equals(digest)); final byte[] expected = entry.getInstanceFromHex("EXPECT"); diff --git a/tst/com/amazon/corretto/crypto/provider/test/TestUtil.java b/tst/com/amazon/corretto/crypto/provider/test/TestUtil.java index 04cfb503..bafeaf5b 100644 --- a/tst/com/amazon/corretto/crypto/provider/test/TestUtil.java +++ b/tst/com/amazon/corretto/crypto/provider/test/TestUtil.java @@ -94,6 +94,14 @@ public static boolean isFips() { return NATIVE_PROVIDER.isFips(); } + public static boolean isExperimentalFips() { + return NATIVE_PROVIDER.isExperimentalFips(); + } + + public static boolean supportsExtraKdfs() { + return isExperimentalFips() || !isFips(); + } + public static byte[] intArrayToByteArray(final int[] array) { final byte[] result = new byte[array.length]; for (int i = 0; i != array.length; i++) {