Skip to content

Commit

Permalink
Experimental FIPS build
Browse files Browse the repository at this point in the history
+ Experimental FIPS mode allows one to use ACCP in FIPS mode with a
  non-FIPS branch of AWS-LC.
+ The versions of AWS-LC and AWS-LC-FIPS are also updated.
  • Loading branch information
amirhosv committed Sep 10, 2024
1 parent 1fb4cbd commit cc4b792
Show file tree
Hide file tree
Showing 13 changed files with 102 additions and 23 deletions.
11 changes: 10 additions & 1 deletion 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,9 @@ if(FIPS)
set(TEST_FIPS_PROPERTY "-DFIPS=true")
else()
set(TEST_FIPS_PROPERTY "-DFIPS=false")
endif()

if(EXPERIMENTAL_FIPS OR (NOT FIPS))
set(C_SRC ${C_SRC}
csrc/concatenation_kdf.cpp
csrc/counter_kdf.cpp)
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
13 changes: 8 additions & 5 deletions tst/com/amazon/corretto/crypto/provider/test/CounterKdfTest.java
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

0 comments on commit cc4b792

Please sign in to comment.