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

Implement FIPSCapable #245

Merged
merged 4 commits into from
Jan 15, 2025
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
47 changes: 42 additions & 5 deletions openssl.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,37 @@ func FIPS() bool {
}
}

// FIPSCapable returns true if the provider used by default matches the `fips=yes` query.
// It is useful for checking whether OpenSSL is capable of running in FIPS mode regardless
// of whether FIPS mode is explicitly enabled. For example, Azure Linux 3 doesn't set the
// `fips=yes` query in the default properties, but sets the default provider to be SCOSSL,
// which is FIPS-capable.
//
// Considerations:
// - Multiple calls to FIPSCapable can return different values if [SetFIPS] is called in between.
// - Can return true even if [FIPS] returns false, because [FIPS] also checks whether
// the default properties contain `fips=yes`.
// - When using OpenSSL 3, will always return true if [FIPS] returns true.
// - When using OpenSSL 1, Will always return the same value as [FIPS].
// - OpenSSL 3 doesn't provide a way to know if a provider is FIPS-capable. This function uses
// some heuristics that should be treated as an implementation detail that may change in the future.
func FIPSCapable() bool {
if FIPS() {
return true
}
if vMajor == 3 {
// Load the provider with and without the `fips=yes` query.
// If the providers are the same, then the default provider is FIPS-capable.
dagood marked this conversation as resolved.
Show resolved Hide resolved
provFIPS := sha256Provider(propFIPS)
if provFIPS == nil {
return false
}
provDefault := sha256Provider(nil)
return provFIPS == provDefault
}
return false
}

// 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 {
Expand Down Expand Up @@ -193,16 +224,22 @@ func SetFIPS(enable bool) error {
}
}

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

// proveSHA256 checks if the SHA-256 algorithm is available
// using the given properties.
func proveSHA256(props *C.char) bool {
return sha256Provider(props) != nil
}

// noescape hides a pointer from escape analysis. noescape is
Expand Down
13 changes: 13 additions & 0 deletions openssl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func TestMain(m *testing.M) {
_ = openssl.SetFIPS(true) // Skip the error as we still want to run the tests on machines without FIPS support.
fmt.Println("OpenSSL version:", openssl.VersionText())
fmt.Println("FIPS enabled:", openssl.FIPS())
fmt.Println("FIPS capable:", openssl.FIPSCapable())
status := m.Run()
for range 5 {
// Run GC a few times to avoid false positives in leak detection.
Expand Down Expand Up @@ -133,3 +134,15 @@ func TestSetFIPS(t *testing.T) {
t.Skip("FIPS mode is not supported")
}
}

func TestFIPSCapable(t *testing.T) {
got := openssl.FIPSCapable()
want := openssl.FIPS()
if !want && openssl.SymCryptProviderAvailable() {
// The SymCrypt provider is FIPS-capable.
want = true
}
if got != want {
t.Fatalf("FIPSCapable mismatch: want %v, got %v", want, got)
}
}
Loading