Skip to content

Commit 387cd1d

Browse files
committed
fips: check provided tuf crypto for fips compliance
1 parent 8e23307 commit 387cd1d

File tree

3 files changed

+381
-2
lines changed

3 files changed

+381
-2
lines changed

internal/controller/tuf/actions/resolve_keys.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ import (
1010
"github.com/securesign/operator/internal/action"
1111
"github.com/securesign/operator/internal/constants"
1212
"github.com/securesign/operator/internal/labels"
13+
cryptoutil "github.com/securesign/operator/internal/utils/crypto"
1314
k8sutils "github.com/securesign/operator/internal/utils/kubernetes"
1415
"k8s.io/apimachinery/pkg/api/equality"
1516
"k8s.io/apimachinery/pkg/api/meta"
1617
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"sigs.k8s.io/controller-runtime/pkg/client"
1719
)
1820

1921
func NewResolveKeysAction() action.Action[*rhtasv1alpha1.Tuf] {
@@ -93,12 +95,19 @@ func (i resolveKeysAction) handleKey(ctx context.Context, instance *rhtasv1alpha
9395
return nil, err
9496
}
9597
key.SecretRef = sks
96-
return key, nil
9798
case key.SecretRef != nil:
98-
return key, nil
99+
// continue to validation
99100
default:
100101
return nil, fmt.Errorf("unable to resolve %s key. Enable autodiscovery or set secret reference", key.Name)
101102
}
103+
104+
if cryptoutil.FIPSEnabled {
105+
if err := validateKey(i.Client, instance.Namespace, key); err != nil {
106+
return nil, fmt.Errorf("key %s is not FIPS-compliant: %w", key.Name, err)
107+
}
108+
}
109+
110+
return key, nil
102111
}
103112

104113
func (i resolveKeysAction) discoverSecret(ctx context.Context, namespace string, key *rhtasv1alpha1.TufKey) (*rhtasv1alpha1.SecretKeySelector, error) {
@@ -123,3 +132,23 @@ func (i resolveKeysAction) discoverSecret(ctx context.Context, namespace string,
123132

124133
return nil, errors.New("secret not found")
125134
}
135+
136+
func validateKey(cli client.Client, namespace string, key *rhtasv1alpha1.TufKey) error {
137+
if key.SecretRef == nil {
138+
return errors.New("secret reference is not set")
139+
}
140+
141+
data, err := k8sutils.GetSecretData(cli, namespace, key.SecretRef)
142+
if err != nil {
143+
return err
144+
}
145+
146+
switch key.Name {
147+
case "rekor.pub", "ctfe.pub":
148+
return cryptoutil.ValidatePublicKeyPEM(data)
149+
case "fulcio_v1.crt.pem", "tsa.certchain.pem":
150+
return cryptoutil.ValidateCertificatePEM(data)
151+
default:
152+
return fmt.Errorf("unsupported key %q", key.Name)
153+
}
154+
}

internal/controller/tuf/actions/resolve_keys_test.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ package actions
22

33
import (
44
"context"
5+
"crypto/elliptic"
56
"testing"
67

78
"github.com/go-logr/logr"
89
. "github.com/onsi/gomega"
10+
"github.com/onsi/gomega/gstruct"
911
"github.com/securesign/operator/api/v1alpha1"
1012
common "github.com/securesign/operator/internal/action"
1113
"github.com/securesign/operator/internal/constants"
1214
"github.com/securesign/operator/internal/labels"
15+
cryptoutil "github.com/securesign/operator/internal/utils/crypto"
16+
fipsTest "github.com/securesign/operator/internal/utils/crypto/test"
1317
v1 "k8s.io/api/core/v1"
1418
"k8s.io/apimachinery/pkg/api/meta"
1519
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -171,3 +175,59 @@ func TestKeyDelete(t *testing.T) {
171175

172176
g.Expect(meta.IsStatusConditionTrue(instance.Status.Conditions, "ctfe.pub")).To(BeTrue())
173177
}
178+
179+
func TestKeyValidationFailsInFIPS(t *testing.T) {
180+
g := NewWithT(t)
181+
cryptoutil.FIPSEnabled = true
182+
t.Cleanup(func() {
183+
cryptoutil.FIPSEnabled = false
184+
})
185+
186+
invalidPub, _, _, err := fipsTest.GenerateECCertificatePEM(false, "", elliptic.P224())
187+
g.Expect(err).ToNot(HaveOccurred())
188+
189+
g.Expect(testAction.Client.Create(testContext, &v1.Secret{
190+
ObjectMeta: metav1.ObjectMeta{
191+
Name: "invalid",
192+
Namespace: t.Name(),
193+
},
194+
Data: map[string][]byte{"key": invalidPub},
195+
})).To(Succeed())
196+
197+
instance := &v1alpha1.Tuf{
198+
ObjectMeta: metav1.ObjectMeta{
199+
Name: "tuf",
200+
Namespace: t.Name(),
201+
},
202+
Spec: v1alpha1.TufSpec{
203+
Keys: []v1alpha1.TufKey{
204+
{
205+
Name: "rekor.pub",
206+
SecretRef: &v1alpha1.SecretKeySelector{
207+
LocalObjectReference: v1alpha1.LocalObjectReference{
208+
Name: "invalid",
209+
},
210+
Key: "key",
211+
},
212+
},
213+
},
214+
},
215+
Status: v1alpha1.TufStatus{
216+
Conditions: []metav1.Condition{{
217+
Type: constants.Ready,
218+
Reason: constants.Pending,
219+
Status: metav1.ConditionFalse,
220+
}},
221+
},
222+
}
223+
224+
testAction.Handle(testContext, instance)
225+
226+
g.Expect(meta.IsStatusConditionFalse(instance.Status.Conditions, "rekor.pub")).To(BeTrue())
227+
g.Expect(meta.FindStatusCondition(instance.Status.Conditions, "rekor.pub")).To(
228+
gstruct.PointTo(SatisfyAll(
229+
HaveField("Reason", Equal(constants.Failure)),
230+
HaveField("Message", ContainSubstring("FIPS")),
231+
)),
232+
)
233+
}

0 commit comments

Comments
 (0)