diff --git a/api/s2n.h b/api/s2n.h index 43ad5e085ce..15f6dbf9c7e 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -241,6 +241,27 @@ S2N_API extern int s2n_init(void); */ S2N_API extern int s2n_cleanup(void); +typedef enum { + S2N_FIPS_MODE_DISABLED = 0, + S2N_FIPS_MODE_ENABLED, +} s2n_fips_mode; + +/** + * Determines whether s2n-tls is operating in FIPS mode. + * + * s2n-tls enters FIPS mode on initialization when the linked libcrypto has FIPS mode enabled. Some + * libcryptos, such as AWS-LC-FIPS, have FIPS mode enabled by default. With other libcryptos, such + * as OpenSSL, FIPS mode must be enabled before initialization by calling `FIPS_mode_set()`. + * + * s2n-tls MUST be linked to a FIPS libcrypto and MUST be in FIPS mode in order to comply with FIPS + * requirements. Applications desiring FIPS compliance should use this API to ensure that s2n-tls + * has been properly linked with a FIPS libcrypto and has successfully entered FIPS mode. + * + * @param fips_mode Set to the FIPS mode of s2n-tls. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure. + */ +S2N_API extern int s2n_get_fips_mode(s2n_fips_mode *fips_mode); + /** * Creates a new s2n_config object. This object can (and should) be associated with many connection * objects. diff --git a/crypto/s2n_fips.c b/crypto/s2n_fips.c index 29229dd607d..ff73433a6ce 100644 --- a/crypto/s2n_fips.c +++ b/crypto/s2n_fips.c @@ -17,11 +17,14 @@ #include +#include "utils/s2n_init.h" +#include "utils/s2n_safety.h" + #if defined(S2N_INTERN_LIBCRYPTO) && defined(OPENSSL_FIPS) #error "Interning with OpenSSL fips-validated libcrypto is not currently supported. See https://github.com/aws/s2n-tls/issues/2741" #endif -static int s2n_fips_mode = 0; +static bool s2n_fips_mode_enabled = false; /* FIPS mode can be checked if OpenSSL was configured and built for FIPS which then defines OPENSSL_FIPS. * @@ -46,17 +49,25 @@ bool s2n_libcrypto_is_fips(void) int s2n_fips_init(void) { - s2n_fips_mode = 0; - - if (s2n_libcrypto_is_fips()) { - s2n_fips_mode = 1; - } - - return 0; + s2n_fips_mode_enabled = s2n_libcrypto_is_fips(); + return S2N_SUCCESS; } /* Return 1 if FIPS mode is enabled, 0 otherwise. FIPS mode must be enabled prior to calling s2n_init(). */ -int s2n_is_in_fips_mode(void) +bool s2n_is_in_fips_mode(void) +{ + return s2n_fips_mode_enabled; +} + +int s2n_get_fips_mode(s2n_fips_mode *fips_mode) { - return s2n_fips_mode; + POSIX_ENSURE_REF(fips_mode); + *fips_mode = S2N_FIPS_MODE_DISABLED; + POSIX_ENSURE(s2n_is_initialized(), S2N_ERR_NOT_INITIALIZED); + + if (s2n_is_in_fips_mode()) { + *fips_mode = S2N_FIPS_MODE_ENABLED; + } + + return S2N_SUCCESS; } diff --git a/crypto/s2n_fips.h b/crypto/s2n_fips.h index f1047d52ae6..a353589199d 100644 --- a/crypto/s2n_fips.h +++ b/crypto/s2n_fips.h @@ -21,7 +21,7 @@ #pragma once int s2n_fips_init(void); -int s2n_is_in_fips_mode(void); +bool s2n_is_in_fips_mode(void); bool s2n_libcrypto_is_fips(void); struct s2n_cipher_suite; diff --git a/tests/cbmc/stubs/s2n_is_in_fips_mode.c b/tests/cbmc/stubs/s2n_is_in_fips_mode.c index 8a76d11bc6d..7ee61b05510 100644 --- a/tests/cbmc/stubs/s2n_is_in_fips_mode.c +++ b/tests/cbmc/stubs/s2n_is_in_fips_mode.c @@ -17,18 +17,18 @@ #include "crypto/s2n_fips.h" -static int flag = 0; -static int s2n_fips_mode = 0; +static bool flag = 0; +static bool s2n_fips_mode_enabled = 0; /** * Return 1 if FIPS mode is set, 0 otherwise, * where FIPS mode is set nondeterministically on first call. */ -int s2n_is_in_fips_mode() +bool s2n_is_in_fips_mode() { if (flag == 0) { - s2n_fips_mode = nondet_bool() ? 1 : 0; - flag = 1; + s2n_fips_mode_enabled = nondet_bool() ? 1 : 0; + flag = 1; } - return s2n_fips_mode; + return s2n_fips_mode_enabled; } diff --git a/tests/unit/s2n_build_test.c b/tests/unit/s2n_build_test.c index 75195fe0d2a..e7f40949d15 100644 --- a/tests/unit/s2n_build_test.c +++ b/tests/unit/s2n_build_test.c @@ -84,6 +84,18 @@ int main() END_TEST(); } + /* Ensure that FIPS mode is enabled when linked to AWS-LC-FIPS, and disabled when linked to AWS-LC */ + if (strstr(s2n_libcrypto, "awslc") != NULL) { + s2n_fips_mode fips_mode = S2N_FIPS_MODE_DISABLED; + EXPECT_SUCCESS(s2n_get_fips_mode(&fips_mode)); + + if (strstr(s2n_libcrypto, "fips") != NULL) { + EXPECT_EQUAL(fips_mode, S2N_FIPS_MODE_ENABLED); + } else { + EXPECT_EQUAL(fips_mode, S2N_FIPS_MODE_DISABLED); + } + } + char s2n_libcrypto_copy[MAX_LIBCRYPTO_NAME_LEN] = { 0 }; EXPECT_TRUE(strlen(s2n_libcrypto) < MAX_LIBCRYPTO_NAME_LEN); EXPECT_OK(s2n_test_lowercase_copy(s2n_libcrypto, &s2n_libcrypto_copy[0], s2n_array_len(s2n_libcrypto_copy))); diff --git a/tests/unit/s2n_fips_test.c b/tests/unit/s2n_fips_test.c new file mode 100644 index 00000000000..c4e5bdc134e --- /dev/null +++ b/tests/unit/s2n_fips_test.c @@ -0,0 +1,53 @@ +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "crypto/s2n_fips.h" + +#include "api/s2n.h" +#include "s2n_test.h" + +int main() +{ + BEGIN_TEST_NO_INIT(); + + /* s2n_get_fips_mode() fails before init */ + { + s2n_fips_mode fips_mode = S2N_FIPS_MODE_ENABLED; + EXPECT_FAILURE_WITH_ERRNO(s2n_get_fips_mode(&fips_mode), S2N_ERR_NOT_INITIALIZED); + EXPECT_EQUAL(fips_mode, S2N_FIPS_MODE_DISABLED); + } + + EXPECT_SUCCESS(s2n_init()); + + /* Test s2n_get_fips_mode() after init */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_get_fips_mode(NULL), S2N_ERR_NULL); + + /* FIPS mode matches s2n_is_in_fips_mode() */ + { + s2n_fips_mode fips_mode = S2N_FIPS_MODE_DISABLED; + EXPECT_SUCCESS(s2n_get_fips_mode(&fips_mode)); + + if (s2n_is_in_fips_mode()) { + EXPECT_EQUAL(fips_mode, S2N_FIPS_MODE_ENABLED); + } else { + EXPECT_EQUAL(fips_mode, S2N_FIPS_MODE_DISABLED); + } + } + } + + END_TEST(); +}