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

[ms-go1.22-support] Support third-party providers in FIPS and SetFIPS #223

Merged
merged 1 commit into from
Nov 14, 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
2 changes: 2 additions & 0 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ package openssl
var ErrOpen = errOpen

var TestNotMarshalable = &testNotMarshalable

var IsProviderAvailable = isProviderAvailable
85 changes: 61 additions & 24 deletions openssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,35 +89,49 @@ func VersionText() string {
var (
providerNameFips = C.CString("fips")
providerNameDefault = C.CString("default")
propFIPS = C.CString("fips=yes")
propNoFIPS = C.CString("-fips")

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 {
// Check if the default properties contain `fips=1`.
if C.go_openssl_EVP_default_properties_is_fips_enabled(nil) != 1 {
// 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.
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
// 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.
return proveSHA256(nil)
default:
panic(errUnsupportedVersion())
}
}

// SetFIPS enables or disables FIPS mode.
//
// For OpenSSL 3, the `fips` provider is loaded if enabled is true,
// else the `default` provider is loaded.
func SetFIPS(enabled bool) error {
// For OpenSSL 3, if there is no provider available that supports FIPS mode,
// SetFIPS will try to load a built-in provider that supports FIPS mode.
func SetFIPS(enable bool) error {
if FIPS() == enable {
// Already in the desired state.
return nil
}
var mode C.int
if enabled {
if enable {
mode = C.int(1)
} else {
mode = C.int(0)
Expand All @@ -129,32 +143,55 @@ func SetFIPS(enabled bool) error {
}
return nil
case 3:
var provName *C.char
if enabled {
var shaProps, provName *C.char
if enable {
shaProps = propFIPS
provName = providerNameFips
} else {
shaProps = propNoFIPS
provName = providerNameDefault
}
// Check if there is any provider that matches props.
if C.go_openssl_OSSL_PROVIDER_available(nil, provName) != 1 {
// If not, fallback to provName provider.
if C.go_openssl_OSSL_PROVIDER_load(nil, provName) == nil {
return newOpenSSLError("OSSL_PROVIDER_try_load")
}
// Make sure we now have a provider available.
if C.go_openssl_OSSL_PROVIDER_available(nil, provName) != 1 {
return fail("SetFIPS(" + strconv.FormatBool(enabled) + ") not supported")
if !proveSHA256(shaProps) {
// There is no provider available that supports the desired FIPS mode.
// Try to load the built-in provider associated with the given mode.
if C.go_openssl_OSSL_PROVIDER_try_load(nil, provName, 1) == nil {
// The built-in provider was not loaded successfully, we can't enable FIPS mode.
C.go_openssl_ERR_clear_error()
return errors.New("openssl: FIPS mode not supported by any provider")
}
}
if C.go_openssl_EVP_default_properties_enable_fips(nil, mode) != 1 {
return newOpenSSLError("openssl: EVP_default_properties_enable_fips")
return newOpenSSLError("EVP_default_properties_enable_fips")
}
return nil
default:
panic(errUnsupportedVersion())
}
}

// proveSHA256 checks if the SHA-256 algorithm is available
// using the given properties.
func proveSHA256(props *C.char) bool {
md := C.go_openssl_EVP_MD_fetch(nil, algorithmSHA256, props)
if md == nil {
C.go_openssl_ERR_clear_error()
return false
}
C.go_openssl_EVP_MD_free(md)
return true
}

// isProviderAvailable checks if the provider with the given name is available.
// This function is used in export_test.go, but must be defined here as test files can't access C functions.
func isProviderAvailable(name string) bool {
if vMajor == 1 {
return false
}
providerName := C.CString(name)
defer C.free(unsafe.Pointer(providerName))
return C.go_openssl_OSSL_PROVIDER_available(nil, providerName) == 1
}

// noescape hides a pointer from escape analysis. noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input. noescape is inlined and currently
Expand Down
41 changes: 41 additions & 0 deletions openssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,44 @@ func TestCheckVersion(t *testing.T) {
t.Fatalf("FIPS mismatch: want %v, got %v", want, fips)
}
}

func TestSetFIPS(t *testing.T) {
fipsEnabled := openssl.FIPS()
t.Cleanup(func() {
// Restore the previous FIPS mode.
err := openssl.SetFIPS(fipsEnabled)
if err != nil {
t.Fatal(err)
}
})

if err := openssl.SetFIPS(fipsEnabled); err != nil {
// Test that we can set FIPS mode to the current state
// without error.
t.Fatalf("SetFIPS(%v) failed: %v", fipsEnabled, err)
}
if got := openssl.FIPS(); got != fipsEnabled {
// Test that the FIPS mode hasn't been changed by the
// previous SetFIPS call.
t.Fatalf("FIPS mode mismatch: want %v, got %v", fipsEnabled, got)
}

if fipsEnabled &&
openssl.IsProviderAvailable("default") {
// Test that we can disable FIPS mode if it was enabled
// when the built-in provider is available.
err := openssl.SetFIPS(false)
if err != nil {
t.Fatalf("SetFIPS(false) failed: %v", err)
}
} else if !fipsEnabled && openssl.IsProviderAvailable("fips") {
// Test that we can enable FIPS mode if it was disabled
// when the provider is known to support FIPS mode.
err := openssl.SetFIPS(true)
if err != nil {
t.Fatalf("SetFIPS(true) failed: %v", err)
}
} else {
t.Skip("FIPS mode is not supported")
}
}
3 changes: 2 additions & 1 deletion shims.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ typedef void* GO_SHA_CTX_PTR;
// #endif
#define FOR_ALL_OPENSSL_FUNCTIONS \
DEFINEFUNC(void, ERR_error_string_n, (unsigned long e, char *buf, size_t len), (e, buf, len)) \
DEFINEFUNC(void, ERR_clear_error, (void), ()) \
DEFINEFUNC_LEGACY_1(unsigned long, ERR_get_error_line, (const char **file, int *line), (file, line)) \
DEFINEFUNC_3_0(unsigned long, ERR_get_error_all, (const char **file, int *line, const char **func, const char **data, int *flags), (file, line, func, data, flags)) \
DEFINEFUNC_RENAMED_1_1(const char *, OpenSSL_version, SSLeay_version, (int type), (type)) \
Expand All @@ -188,7 +189,7 @@ DEFINEFUNC_LEGACY_1(int, FIPS_mode_set, (int r), (r)) \
DEFINEFUNC_3_0(int, EVP_default_properties_is_fips_enabled, (GO_OSSL_LIB_CTX_PTR libctx), (libctx)) \
DEFINEFUNC_3_0(int, EVP_default_properties_enable_fips, (GO_OSSL_LIB_CTX_PTR libctx, int enable), (libctx, enable)) \
DEFINEFUNC_3_0(int, OSSL_PROVIDER_available, (GO_OSSL_LIB_CTX_PTR libctx, const char *name), (libctx, name)) \
DEFINEFUNC_3_0(GO_OSSL_PROVIDER_PTR, OSSL_PROVIDER_load, (GO_OSSL_LIB_CTX_PTR libctx, const char *name), (libctx, name)) \
DEFINEFUNC_3_0(GO_OSSL_PROVIDER_PTR, OSSL_PROVIDER_try_load, (GO_OSSL_LIB_CTX_PTR libctx, const char *name, int retain_fallbacks), (libctx, name, retain_fallbacks)) \
DEFINEFUNC_3_0(const char *, OSSL_PROVIDER_get0_name, (const GO_OSSL_PROVIDER_PTR prov), (prov)) \
DEFINEFUNC_3_0(GO_EVP_MD_PTR, EVP_MD_fetch, (GO_OSSL_LIB_CTX_PTR ctx, const char *algorithm, const char *properties), (ctx, algorithm, properties)) \
DEFINEFUNC_3_0(void, EVP_MD_free, (GO_EVP_MD_PTR md), (md)) \
Expand Down
Loading