diff --git a/pkg/generator/certificate_reconciler.go b/pkg/generator/certificate_reconciler.go index 1c99fc1a9..62eef8619 100644 --- a/pkg/generator/certificate_reconciler.go +++ b/pkg/generator/certificate_reconciler.go @@ -5,6 +5,7 @@ package generator import ( "context" + "crypto/fips140" "fmt" sgv1alpha1 "carvel.dev/secretgen-controller/pkg/apis/secretgen/v1alpha1" @@ -185,7 +186,15 @@ func (r *CertificateReconciler) generate(ctx context.Context, params certParams, gen := cfgtypes.NewCertificateGenerator(singleCertLoader{caCertSecret}) - certVal, err := gen.Generate(params) + // cfgtypes.CertificateGenerator.Generate uses crypto/sha1 to derive the certificate's + // SubjectKeyId (RFC 5280 4.2.1.2, method (1)): a non-cryptographic key identifier used + // for chain-building/lookup, not for signing or trust decisions. WithoutEnforcement + // scopes the FIPS 140-3 "only" exemption to just this call, so it has no effect when + // strict enforcement isn't active. + var certVal interface{} + fips140.WithoutEnforcement(func() { + certVal, err = gen.Generate(params) + }) if err != nil { return cfgtypes.CertResponse{}, err } diff --git a/pkg/generator/fips140_internal_test.go b/pkg/generator/fips140_internal_test.go new file mode 100644 index 000000000..d50a19c19 --- /dev/null +++ b/pkg/generator/fips140_internal_test.go @@ -0,0 +1,57 @@ +// Copyright 2024 The Carvel Authors. +// SPDX-License-Identifier: Apache-2.0 + +package generator + +import ( + "context" + "crypto/fips140" + "testing" + + sgv1alpha1 "carvel.dev/secretgen-controller/pkg/apis/secretgen/v1alpha1" +) + +// These tests only exercise anything meaningful when run with +// GOFIPS140=v1.0.0 GODEBUG=fips140=only, which enables strict FIPS 140-3 +// enforcement (crypto/fips140.Enforced() reports it at runtime). Without +// that, they degrade to a skip so they don't add noise to a normal `go test +// ./...` run. + +func TestSSHKeyReconciler_GenerateUnderFIPS140Only(t *testing.T) { + if !fips140.Enforced() { + t.Skip("run with GOFIPS140=v1.0.0 GODEBUG=fips140=only to exercise strict FIPS 140-3 enforcement") + } + + r := &SSHKeyReconciler{} + + sshKey, err := r.generate(&sgv1alpha1.SSHKey{}) + if err != nil { + t.Fatalf("generate: %v", err) + } + if sshKey.PrivateKey == "" || sshKey.PublicKey == "" || sshKey.PublicKeyFingerprint == "" { + t.Fatalf("generate returned incomplete SSHKey: %+v", sshKey) + } +} + +func TestCertificateReconciler_GenerateUnderFIPS140Only(t *testing.T) { + if !fips140.Enforced() { + t.Skip("run with GOFIPS140=v1.0.0 GODEBUG=fips140=only to exercise strict FIPS 140-3 enforcement") + } + + r := &CertificateReconciler{} + cert := &sgv1alpha1.Certificate{ + Spec: sgv1alpha1.CertificateSpec{ + CommonName: "test-ca", + IsCA: true, + }, + } + params := newCertParams(cert) + + certResp, err := r.generate(context.Background(), params, cert) + if err != nil { + t.Fatalf("generate: %v", err) + } + if certResp.Certificate == "" || certResp.PrivateKey == "" { + t.Fatalf("generate returned incomplete CertResponse: %+v", certResp) + } +} diff --git a/pkg/generator/ssh_key_reconciler.go b/pkg/generator/ssh_key_reconciler.go index 3fdeff8b8..959316a26 100644 --- a/pkg/generator/ssh_key_reconciler.go +++ b/pkg/generator/ssh_key_reconciler.go @@ -5,6 +5,7 @@ package generator import ( "context" + "crypto/fips140" "fmt" sgv1alpha1 "carvel.dev/secretgen-controller/pkg/apis/secretgen/v1alpha1" @@ -122,7 +123,15 @@ func (r *SSHKeyReconciler) generate(sshKey *sgv1alpha1.SSHKey) (cfgtypes.SSHKey, gen := cfgtypes.NewSSHKeyGenerator() // TODO allow type and number of bits? - sshKeyVal, err := gen.Generate(nil) + // cfgtypes.SSHKeyGenerator.Generate uses crypto/md5 to compute PublicKeyFingerprint, + // a display-only identifier (same convention as `ssh-keygen -l -E md5`), not a + // cryptographic operation. WithoutEnforcement scopes the FIPS 140-3 "only" exemption + // to just this call, so it has no effect when strict enforcement isn't active. + var sshKeyVal interface{} + var err error + fips140.WithoutEnforcement(func() { + sshKeyVal, err = gen.Generate(nil) + }) if err != nil { return cfgtypes.SSHKey{}, err }