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

Refactor openssl.FIPS to support third-party FIPS providers #184

Open
wants to merge 4 commits into
base: v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
37 changes: 29 additions & 8 deletions goopenssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ FOR_ALL_OPENSSL_FUNCTIONS
#undef DEFINEFUNC_RENAMED_1_1
#undef DEFINEFUNC_RENAMED_3_0

// go_openssl_fips_enabled returns 1 if FIPS mode is enabled, 0 otherwise.
// See openssl.FIPS for details about its implementation.
//
// This function is reimplemented here because openssl.FIPS assumes that
// all the OpenSSL bindings are loaded, that is, go_openssl_load_functions has
// already been called. On the other hand, go_openssl_fips_enabled is called from
// openssl.CheckFIPS, which is used to check if a given OpenSSL shared library
qmuntal marked this conversation as resolved.
Show resolved Hide resolved
// exists and is FIPS compliant. That shared library might not be the one that
// was passed to go_openssl_load_functions, or it might not even have been called at all.
//
// It is written in C because it is not possible to directly call C function pointers
// retrieved using dlsym from Go.
int
go_openssl_fips_enabled(void* handle)
{
Expand All @@ -45,15 +57,24 @@ go_openssl_fips_enabled(void* handle)
return FIPS_mode();

// For OpenSSL 3.x.
int (*EVP_default_properties_is_fips_enabled)(void*);
int (*OSSL_PROVIDER_available)(void*, const char*);
EVP_default_properties_is_fips_enabled = (int (*)(void*))dlsym(handle, "EVP_default_properties_is_fips_enabled");
OSSL_PROVIDER_available = (int (*)(void*, const char*))dlsym(handle, "OSSL_PROVIDER_available");
if (EVP_default_properties_is_fips_enabled != NULL && OSSL_PROVIDER_available != NULL &&
EVP_default_properties_is_fips_enabled(NULL) == 1 && OSSL_PROVIDER_available(NULL, "fips") == 1)
return 1;
int (*EVP_default_properties_is_fips_enabled)(void*) = (int (*)(void*))dlsym(handle, "EVP_default_properties_is_fips_enabled");
void *(*EVP_MD_fetch)(void*, const char*, const char*) = (void* (*)(void*, const char*, const char*))dlsym(handle, "EVP_MD_fetch");
void (*EVP_MD_free)(void*) = (void (*)(void*))dlsym(handle, "EVP_MD_free");

return 0;
if (EVP_default_properties_is_fips_enabled == NULL || EVP_MD_fetch == NULL || EVP_MD_free == NULL) {
// Shouldn't happen, but if it does, we can't determine if FIPS mode is enabled.
return 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A false negative for FIPS mode does seem nearly harmless, but it also seems like it would be pretty simple to return and handle -1 or 2 or something just to be sure.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only thing with the current openssl.CheckVersion API is to log the error or panic, as it doesn't return any error value. I wouldn't say it is a false negative, if those functions are not available then we won't be able to use the shared library properly, even less in FIPS mode.

}

if (EVP_default_properties_is_fips_enabled(NULL) != 1)
return 0;

void *md = EVP_MD_fetch(NULL, "SHA2-256", NULL);
if (md == NULL)
return 0;

EVP_MD_free(md);
return 1;
}

// Load all the functions stored in FOR_ALL_OPENSSL_FUNCTIONS
Expand Down
29 changes: 21 additions & 8 deletions openssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,23 +96,36 @@ func VersionText() string {
var (
providerNameFips = C.CString("fips")
providerNameDefault = C.CString("default")

algorithmSHA256 = C.CString("SHA2-256")
)

// FIPS returns true if OpenSSL is running in FIPS mode, else returns false.
// FIPS returns true if OpenSSL is running in FIPS mode and there is
// a provider available that supports FIPS. It returns false otherwise.
func FIPS() bool {
switch vMajor {
case 1:
return C.go_openssl_FIPS_mode() == 1
case 3:
// If FIPS is not enabled via default properties, then we are sure FIPS is not used.
if C.go_openssl_EVP_default_properties_is_fips_enabled(nil) == 0 {
// FIPS is not enabled via default properties (i.e. `fips=1`), then we are sure FIPS is not used.
// Note that it is still possible that the provider used by default is FIPS-compliant,
// but that wouldn't be a system or user requirement.
qmuntal marked this conversation as resolved.
Show resolved Hide resolved
if C.go_openssl_EVP_default_properties_is_fips_enabled(nil) != 1 {
return false
}
// Check if the SHA-256 algorithm is available. If it is, then we can be sure that there is a provider available that matches
// the `fips=1` query. Most notably, this works for the common case of using the built-in FIPS provider.
//
// Note that this approach has a small chance of false negative if the FIPS provider doesn't provide the SHA-256 algorithm,
// but that is highly unlikely because SHA-256 is one of the most common algorithms and fundamental to many cryptographic operations.
// It also has a small chance of false positive if the FIPS provider implements the SHA-256 algorithm but not the other algorithms
// used by the caller application, but that is also unlikely because the FIPS provider should provide all common algorithms.
md := C.go_openssl_EVP_MD_fetch(nil, algorithmSHA256, nil)
if md == nil {
return false
}
// EVP_default_properties_is_fips_enabled can return true even if the FIPS provider isn't loaded,
// it is only based on the default properties.
// We can be sure that the FIPS provider is available if we can fetch an algorithm, e.g., SHA2-256,
// explicitly setting `fips=yes`.
return C.go_openssl_OSSL_PROVIDER_available(nil, providerNameFips) == 1
C.go_openssl_EVP_MD_free(md)
return true
default:
panic(errUnsupportedVersion())
}
Expand Down
Loading