Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental FIPS build #403

Merged
merged 1 commit into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved
set(C_SRC ${C_SRC}
csrc/concatenation_kdf.cpp
csrc/counter_kdf.cpp)
Expand All @@ -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)
WillChilds-Klein marked this conversation as resolved.
Show resolved Hide resolved
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()

Expand Down Expand Up @@ -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)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/csrc/config.h.in ${JNI_HEADER_DIR}/config.h)
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion aws-lc
23 changes: 19 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -239,9 +246,11 @@ task buildAwsLc {
args "-DCMAKE_INSTALL_PREFIX=${sharedObjectOutDir}"
args "-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON"


if (isFips) {
args '-DFIPS=1'
}

args '.'
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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']
Expand Down
2 changes: 1 addition & 1 deletion csrc/keyutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions csrc/loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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.
*
* <p>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
*
Expand Down
5 changes: 5 additions & 0 deletions src/com/amazon/corretto/crypto/provider/Loader.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
11 changes: 9 additions & 2 deletions tests/ci/run_accp_basic_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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

Expand All @@ -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
9 changes: 7 additions & 2 deletions tests/ci/run_accp_test_integration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ 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
case ${1} in
--fips)
testing_fips=true
;;
--experimental-fips)
testing_experimental_fips=true
;;
*)
echo "${1} is not supported."
exit 1
Expand All @@ -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

Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
}
});
}
Expand All @@ -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(
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
}
});
}
Expand All @@ -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(
Expand All @@ -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");
Expand All @@ -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");
Expand Down
8 changes: 8 additions & 0 deletions tst/com/amazon/corretto/crypto/provider/test/TestUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down
Loading