Skip to content

Commit 3048a85

Browse files
qmuntaldagood
andauthored
Refactor openssl.FIPS to support third-party FIPS providers (#184)
* simplify openssl.FIPS to support SymCrypt * implement stronger guarantees for FIPS() * Update goopenssl.c Co-authored-by: Davis Goodin <[email protected]> * improve comments * update CheckVersion to return exists=false for non-OpenSSL libraries --------- Co-authored-by: Davis Goodin <[email protected]>
1 parent 607303c commit 3048a85

File tree

2 files changed

+59
-18
lines changed

2 files changed

+59
-18
lines changed

goopenssl.c

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ FOR_ALL_OPENSSL_FUNCTIONS
3535
#undef DEFINEFUNC_RENAMED_1_1
3636
#undef DEFINEFUNC_RENAMED_3_0
3737

38+
// go_openssl_fips_enabled returns 1 if FIPS mode is enabled, 0 otherwise.
39+
// As a special case, it returns -1 if it cannot determine if FIPS mode is enabled.
40+
// See openssl.FIPS for details about its implementation.
41+
//
42+
// This function is reimplemented here because openssl.FIPS assumes that
43+
// all the OpenSSL bindings are loaded, that is, go_openssl_load_functions has
44+
// already been called. On the other hand, go_openssl_fips_enabled is called from
45+
// openssl.CheckVersion, which is used to check if a given OpenSSL shared library
46+
// exists and is FIPS compliant. That shared library might not be the one that
47+
// was passed to go_openssl_load_functions, or it might not even have been called at all.
48+
//
49+
// It is written in C because it is not possible to directly call C function pointers
50+
// retrieved using dlsym from Go.
3851
int
3952
go_openssl_fips_enabled(void* handle)
4053
{
@@ -45,15 +58,24 @@ go_openssl_fips_enabled(void* handle)
4558
return FIPS_mode();
4659

4760
// For OpenSSL 3.x.
48-
int (*EVP_default_properties_is_fips_enabled)(void*);
49-
int (*OSSL_PROVIDER_available)(void*, const char*);
50-
EVP_default_properties_is_fips_enabled = (int (*)(void*))dlsym(handle, "EVP_default_properties_is_fips_enabled");
51-
OSSL_PROVIDER_available = (int (*)(void*, const char*))dlsym(handle, "OSSL_PROVIDER_available");
52-
if (EVP_default_properties_is_fips_enabled != NULL && OSSL_PROVIDER_available != NULL &&
53-
EVP_default_properties_is_fips_enabled(NULL) == 1 && OSSL_PROVIDER_available(NULL, "fips") == 1)
54-
return 1;
61+
int (*EVP_default_properties_is_fips_enabled)(void*) = (int (*)(void*))dlsym(handle, "EVP_default_properties_is_fips_enabled");
62+
void *(*EVP_MD_fetch)(void*, const char*, const char*) = (void* (*)(void*, const char*, const char*))dlsym(handle, "EVP_MD_fetch");
63+
void (*EVP_MD_free)(void*) = (void (*)(void*))dlsym(handle, "EVP_MD_free");
5564

56-
return 0;
65+
if (EVP_default_properties_is_fips_enabled == NULL || EVP_MD_fetch == NULL || EVP_MD_free == NULL) {
66+
// Shouldn't happen, but if it does, we can't determine if FIPS mode is enabled.
67+
return -1;
68+
}
69+
70+
if (EVP_default_properties_is_fips_enabled(NULL) != 1)
71+
return 0;
72+
73+
void *md = EVP_MD_fetch(NULL, "SHA2-256", NULL);
74+
if (md == NULL)
75+
return 0;
76+
77+
EVP_MD_free(md);
78+
return 1;
5779
}
5880

5981
// Load all the functions stored in FOR_ALL_OPENSSL_FUNCTIONS

openssl.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,14 @@ func CheckVersion(version string) (exists, fips bool) {
3838
return false, false
3939
}
4040
defer dlclose(handle)
41-
fips = C.go_openssl_fips_enabled(handle) == 1
42-
return true, fips
41+
enabled := C.go_openssl_fips_enabled(handle)
42+
fips = enabled == 1
43+
// If go_openssl_fips_enabled returns -1, it means that all or some of the necessary
44+
// functions are not available. This can be due to the version of OpenSSL being too old,
45+
// too incompatible, or the shared library not being an OpenSSL library. In any case,
46+
// we shouldn't consider this library to be valid for our purposes.
47+
exists = enabled != -1
48+
return
4349
}
4450

4551
// Init loads and initializes OpenSSL from the shared library at path.
@@ -96,23 +102,36 @@ func VersionText() string {
96102
var (
97103
providerNameFips = C.CString("fips")
98104
providerNameDefault = C.CString("default")
105+
106+
algorithmSHA256 = C.CString("SHA2-256")
99107
)
100108

101-
// FIPS returns true if OpenSSL is running in FIPS mode, else returns false.
109+
// FIPS returns true if OpenSSL is running in FIPS mode and there is
110+
// a provider available that supports FIPS. It returns false otherwise.
102111
func FIPS() bool {
103112
switch vMajor {
104113
case 1:
105114
return C.go_openssl_FIPS_mode() == 1
106115
case 3:
107-
// If FIPS is not enabled via default properties, then we are sure FIPS is not used.
108-
if C.go_openssl_EVP_default_properties_is_fips_enabled(nil) == 0 {
116+
// Check if the default properties contain `fips=1`.
117+
if C.go_openssl_EVP_default_properties_is_fips_enabled(nil) != 1 {
118+
// Note that it is still possible that the provider used by default is FIPS-compliant,
119+
// but that wouldn't be a system or user requirement.
120+
return false
121+
}
122+
// Check if the SHA-256 algorithm is available. If it is, then we can be sure that there is a provider available that matches
123+
// the `fips=1` query. Most notably, this works for the common case of using the built-in FIPS provider.
124+
//
125+
// Note that this approach has a small chance of false negative if the FIPS provider doesn't provide the SHA-256 algorithm,
126+
// but that is highly unlikely because SHA-256 is one of the most common algorithms and fundamental to many cryptographic operations.
127+
// It also has a small chance of false positive if the FIPS provider implements the SHA-256 algorithm but not the other algorithms
128+
// used by the caller application, but that is also unlikely because the FIPS provider should provide all common algorithms.
129+
md := C.go_openssl_EVP_MD_fetch(nil, algorithmSHA256, nil)
130+
if md == nil {
109131
return false
110132
}
111-
// EVP_default_properties_is_fips_enabled can return true even if the FIPS provider isn't loaded,
112-
// it is only based on the default properties.
113-
// We can be sure that the FIPS provider is available if we can fetch an algorithm, e.g., SHA2-256,
114-
// explicitly setting `fips=yes`.
115-
return C.go_openssl_OSSL_PROVIDER_available(nil, providerNameFips) == 1
133+
C.go_openssl_EVP_MD_free(md)
134+
return true
116135
default:
117136
panic(errUnsupportedVersion())
118137
}

0 commit comments

Comments
 (0)