Skip to content

Commit c98f6af

Browse files
committed
fips: check provided tsa crypto for fips compliance
1 parent 6755011 commit c98f6af

File tree

4 files changed

+258
-17
lines changed

4 files changed

+258
-17
lines changed

internal/controller/tsa/actions/generate_signer.go

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
tsaUtils "github.com/securesign/operator/internal/controller/tsa/utils"
1717
"github.com/securesign/operator/internal/labels"
1818
"github.com/securesign/operator/internal/utils"
19+
cryptoutil "github.com/securesign/operator/internal/utils/crypto"
1920
"github.com/securesign/operator/internal/utils/kubernetes"
2021
"github.com/securesign/operator/internal/utils/kubernetes/ensure"
2122
v1 "k8s.io/api/core/v1"
@@ -219,22 +220,30 @@ func (g generateSigner) Handle(ctx context.Context, instance *v1alpha1.Timestamp
219220
return g.StatusUpdate(ctx, instance)
220221
}
221222

222-
func (g generateSigner) handleSignerKeys(instance *v1alpha1.TimestampAuthority, config *tsaUtils.TsaCertChainConfig) (*tsaUtils.TsaCertChainConfig, error) {
223+
func (g generateSigner) handleSignerKeys(instance *v1alpha1.TimestampAuthority, config *tsaUtils.TsaCertChainConfig) (*tsaUtils.TsaCertChainConfig, error) { //nolint:gocyclo
223224
if instance.Spec.Signer.File != nil {
224225
if instance.Spec.Signer.File.PrivateKeyRef != nil {
225226
key, err := kubernetes.GetSecretData(g.Client, instance.Namespace, instance.Spec.Signer.File.PrivateKeyRef)
226227
if err != nil {
227228
return nil, err
228229
}
229-
config.LeafPrivateKey = key
230230

231+
var password []byte
231232
if ref := instance.Spec.Signer.File.PasswordRef; ref != nil {
232-
password, err := kubernetes.GetSecretData(g.Client, instance.Namespace, ref)
233+
password, err = kubernetes.GetSecretData(g.Client, instance.Namespace, ref)
233234
if err != nil {
234235
return nil, err
235236
}
236-
config.LeafPrivateKeyPassword = password
237237
}
238+
239+
if cryptoutil.FIPSEnabled {
240+
if err := cryptoutil.ValidatePrivateKeyPEM(key, password); err != nil {
241+
return nil, err
242+
}
243+
}
244+
245+
config.LeafPrivateKey = key
246+
config.LeafPrivateKeyPassword = password
238247
}
239248

240249
if ref := instance.Spec.Signer.CertificateChain.CertificateChainRef; ref == nil {
@@ -257,15 +266,23 @@ func (g generateSigner) handleSignerKeys(instance *v1alpha1.TimestampAuthority,
257266
if err != nil {
258267
return nil, err
259268
}
260-
config.RootPrivateKey = key
261269

262-
if ref := instance.Spec.Signer.CertificateChain.RootCA.PasswordRef; ref != nil {
263-
password, err := kubernetes.GetSecretData(g.Client, instance.Namespace, ref)
270+
var password []byte
271+
if pref := instance.Spec.Signer.CertificateChain.RootCA.PasswordRef; pref != nil {
272+
password, err = kubernetes.GetSecretData(g.Client, instance.Namespace, pref)
264273
if err != nil {
265274
return nil, err
266275
}
267-
config.RootPrivateKeyPassword = password
268276
}
277+
278+
if cryptoutil.FIPSEnabled {
279+
if err := cryptoutil.ValidatePrivateKeyPEM(key, password); err != nil {
280+
return nil, err
281+
}
282+
}
283+
284+
config.RootPrivateKey = key
285+
config.RootPrivateKeyPassword = password
269286
} else {
270287
config.RootPrivateKeyPassword = utils.GeneratePassword(8)
271288
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
@@ -286,17 +303,25 @@ func (g generateSigner) handleSignerKeys(instance *v1alpha1.TimestampAuthority,
286303
if err != nil {
287304
return nil, err
288305
}
289-
config.IntermediatePrivateKeys = append(config.IntermediatePrivateKeys, key)
290306

291-
if ref := intermediateCA.PasswordRef; ref != nil {
292-
password, err := kubernetes.GetSecretData(g.Client, instance.Namespace, ref)
307+
var password []byte
308+
if pref := intermediateCA.PasswordRef; pref != nil {
309+
password, err = kubernetes.GetSecretData(g.Client, instance.Namespace, pref)
293310
if err != nil {
294311
return nil, err
295312
}
296-
config.IntermediatePrivateKeyPasswords = append(config.IntermediatePrivateKeyPasswords, password)
297313
} else {
298-
config.IntermediatePrivateKeyPasswords = append(config.IntermediatePrivateKeyPasswords, []byte(""))
314+
password = []byte("")
315+
}
316+
317+
if cryptoutil.FIPSEnabled {
318+
if err := cryptoutil.ValidatePrivateKeyPEM(key, password); err != nil {
319+
return nil, err
320+
}
299321
}
322+
323+
config.IntermediatePrivateKeys = append(config.IntermediatePrivateKeys, key)
324+
config.IntermediatePrivateKeyPasswords = append(config.IntermediatePrivateKeyPasswords, password)
300325
} else {
301326
config.IntermediatePrivateKeyPasswords = append(config.IntermediatePrivateKeyPasswords, utils.GeneratePassword(8))
302327
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
@@ -317,15 +342,23 @@ func (g generateSigner) handleSignerKeys(instance *v1alpha1.TimestampAuthority,
317342
if err != nil {
318343
return nil, err
319344
}
320-
config.LeafPrivateKey = key
321345

322-
if ref := instance.Spec.Signer.CertificateChain.LeafCA.PasswordRef; ref != nil {
323-
password, err := kubernetes.GetSecretData(g.Client, instance.Namespace, ref)
346+
var password []byte
347+
if pref := instance.Spec.Signer.CertificateChain.LeafCA.PasswordRef; pref != nil {
348+
password, err = kubernetes.GetSecretData(g.Client, instance.Namespace, pref)
324349
if err != nil {
325350
return nil, err
326351
}
327-
config.LeafPrivateKeyPassword = password
328352
}
353+
354+
if cryptoutil.FIPSEnabled {
355+
if err := cryptoutil.ValidatePrivateKeyPEM(key, password); err != nil {
356+
return nil, err
357+
}
358+
}
359+
360+
config.LeafPrivateKey = key
361+
config.LeafPrivateKeyPassword = password
329362
} else {
330363
config.LeafPrivateKeyPassword = utils.GeneratePassword(8)
331364
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)

internal/controller/tsa/actions/generate_signer_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package actions
22

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

89
"github.com/securesign/operator/internal/action"
910
"github.com/securesign/operator/internal/constants"
1011
"github.com/securesign/operator/internal/labels"
12+
cryptoutil "github.com/securesign/operator/internal/utils/crypto"
13+
fipsTest "github.com/securesign/operator/internal/utils/crypto/test"
1114
"github.com/securesign/operator/internal/utils/kubernetes"
1215
"github.com/securesign/operator/test/e2e/support/tas/tsa"
1316
v1 "k8s.io/api/core/v1"
@@ -210,6 +213,58 @@ func Test_SignerHandle(t *testing.T) {
210213
return true
211214
},
212215
},
216+
{
217+
name: "FIPS enabled rejects non-compliant root key",
218+
setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) {
219+
instance.Status.Conditions[0].Reason = constants.Pending
220+
instance.Spec.Signer = rhtasv1alpha1.TimestampAuthoritySigner{
221+
CertificateChain: rhtasv1alpha1.CertificateChain{
222+
RootCA: &rhtasv1alpha1.TsaCertificateAuthority{
223+
OrganizationName: "Red Hat",
224+
PrivateKeyRef: &rhtasv1alpha1.SecretKeySelector{
225+
LocalObjectReference: rhtasv1alpha1.LocalObjectReference{
226+
Name: "tsa-invalid-secret",
227+
},
228+
Key: "rootPrivateKey",
229+
},
230+
PasswordRef: &rhtasv1alpha1.SecretKeySelector{
231+
LocalObjectReference: rhtasv1alpha1.LocalObjectReference{
232+
Name: "tsa-invalid-secret",
233+
},
234+
Key: "rootPrivateKeyPassword",
235+
},
236+
},
237+
},
238+
}
239+
g := NewWithT(t)
240+
_, invalidPriv, _, err := fipsTest.GenerateECCertificatePEM(false, "", elliptic.P224())
241+
g.Expect(err).NotTo(HaveOccurred())
242+
secret := &v1.Secret{
243+
ObjectMeta: metav1.ObjectMeta{
244+
Name: "tsa-invalid-secret",
245+
Namespace: instance.Namespace,
246+
},
247+
Data: map[string][]byte{
248+
"rootPrivateKey": invalidPriv,
249+
},
250+
}
251+
cryptoutil.FIPSEnabled = true
252+
t.Cleanup(func() {
253+
cryptoutil.FIPSEnabled = false
254+
})
255+
256+
cli, act := common.TsaTestSetup(instance, t, nil, NewGenerateSignerAction(), secret)
257+
return cli, act
258+
},
259+
testCase: func(g Gomega, _ action.Action[*rhtasv1alpha1.TimestampAuthority], _ client.WithWatch, instance *rhtasv1alpha1.TimestampAuthority) bool {
260+
cond := meta.FindStatusCondition(instance.Status.Conditions, TSASignerCondition)
261+
g.Expect(cond).NotTo(BeNil(), "TSASignerCondition should be present")
262+
g.Expect(cond.Status).To(Equal(metav1.ConditionFalse), "TSASignerCondition status should be False")
263+
g.Expect(cond.Reason).To(Equal(constants.Failure), "TSASignerCondition reason should be Failure")
264+
265+
return true
266+
},
267+
},
213268
{
214269
name: "user-spec keys and certs",
215270
setup: func(instance *rhtasv1alpha1.TimestampAuthority) (client.WithWatch, action.Action[*rhtasv1alpha1.TimestampAuthority]) {
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//go:build fips
2+
3+
package fips
4+
5+
import (
6+
"crypto/elliptic"
7+
8+
. "github.com/onsi/ginkgo/v2"
9+
. "github.com/onsi/gomega"
10+
"github.com/securesign/operator/api/v1alpha1"
11+
"github.com/securesign/operator/internal/constants"
12+
tsaactions "github.com/securesign/operator/internal/controller/tsa/actions"
13+
fipsTest "github.com/securesign/operator/internal/utils/crypto/test"
14+
"github.com/securesign/operator/test/e2e/support"
15+
"github.com/securesign/operator/test/e2e/support/steps"
16+
"github.com/securesign/operator/test/e2e/support/tas/ctlog"
17+
"github.com/securesign/operator/test/e2e/support/tas/fulcio"
18+
"github.com/securesign/operator/test/e2e/support/tas/rekor"
19+
"github.com/securesign/operator/test/e2e/support/tas/securesign"
20+
"github.com/securesign/operator/test/e2e/support/tas/tsa"
21+
v1 "k8s.io/api/core/v1"
22+
"k8s.io/apimachinery/pkg/api/meta"
23+
"k8s.io/apimachinery/pkg/types"
24+
)
25+
26+
var _ = Describe("Securesign FIPS - TSA Cert chain", Ordered, func() {
27+
28+
cli, _ := support.CreateClient()
29+
30+
var namespace *v1.Namespace
31+
var s *v1alpha1.Securesign
32+
33+
BeforeAll(steps.CreateNamespace(cli, func(new *v1.Namespace) {
34+
namespace = new
35+
}))
36+
37+
BeforeAll(func(ctx SpecContext) {
38+
s = securesign.Create(namespace.Name, "test",
39+
securesign.WithDefaults(),
40+
securesign.WithProvidedCerts(),
41+
func(v *v1alpha1.Securesign) {
42+
v.Spec.Tuf.Keys = []v1alpha1.TufKey{
43+
{
44+
Name: "fulcio_v1.crt.pem",
45+
SecretRef: &v1alpha1.SecretKeySelector{
46+
LocalObjectReference: v1alpha1.LocalObjectReference{
47+
Name: "my-fulcio-secret",
48+
},
49+
Key: "cert",
50+
},
51+
},
52+
{
53+
Name: "rekor.pub",
54+
SecretRef: &v1alpha1.SecretKeySelector{
55+
LocalObjectReference: v1alpha1.LocalObjectReference{
56+
Name: "my-rekor-secret",
57+
},
58+
Key: "public",
59+
},
60+
},
61+
{
62+
Name: "ctfe.pub",
63+
SecretRef: &v1alpha1.SecretKeySelector{
64+
LocalObjectReference: v1alpha1.LocalObjectReference{
65+
Name: "my-ctlog-secret",
66+
},
67+
Key: "public",
68+
},
69+
},
70+
{
71+
Name: "tsa.certchain.pem",
72+
SecretRef: &v1alpha1.SecretKeySelector{
73+
LocalObjectReference: v1alpha1.LocalObjectReference{
74+
Name: "test-tuf-tsa-secret",
75+
},
76+
Key: "certificateChain",
77+
},
78+
},
79+
}
80+
},
81+
)
82+
})
83+
84+
Describe("Reject non-FIPS TSA Cert chain and key then accept FIPS-compliant key", func() {
85+
BeforeAll(func(ctx SpecContext) {
86+
_, invalidPriv, invalidCert, err := fipsTest.GenerateECCertificatePEM(true, "pass", elliptic.P224())
87+
Expect(err).NotTo(HaveOccurred())
88+
89+
Expect(cli.Create(ctx, ctlog.CreateSecret(namespace.Name, "my-ctlog-secret"))).To(Succeed())
90+
Expect(cli.Create(ctx, fulcio.CreateSecret(namespace.Name, "my-fulcio-secret"))).To(Succeed())
91+
Expect(cli.Create(ctx, rekor.CreateSecret(namespace.Name, "my-rekor-secret"))).To(Succeed())
92+
Expect(cli.Create(ctx, tsa.CreateSecrets(namespace.Name, "test-tuf-tsa-secret"))).To(Succeed())
93+
Expect(cli.Create(ctx, tsa.CreateCustomTsaSecret(namespace.Name, "test-tsa-secret", map[string][]byte{
94+
"leafPrivateKey": invalidPriv,
95+
"leafPrivateKeyPassword": []byte("pass"),
96+
"certificateChain": invalidCert,
97+
}))).To(Succeed())
98+
Expect(cli.Create(ctx, s)).To(Succeed())
99+
})
100+
101+
It("TSA reports TSASignerCondition False with Failure reason", func(ctx SpecContext) {
102+
Eventually(func(g Gomega) bool {
103+
t := tsa.Get(ctx, cli, namespace.Name, s.Name)
104+
g.Expect(t).ToNot(BeNil())
105+
c := meta.FindStatusCondition(t.Status.Conditions, tsaactions.TSASignerCondition)
106+
return c != nil && string(c.Status) == "False" && c.Reason == constants.Failure
107+
}).WithContext(ctx).Should(BeTrue())
108+
})
109+
110+
It("Update TSA secret to FIPS-compliant EC P256/P384 and verify readiness", func(ctx SpecContext) {
111+
Eventually(func(g Gomega) error {
112+
Expect(cli.Get(ctx, types.NamespacedName{Name: s.Name, Namespace: namespace.Name}, s)).To(Succeed())
113+
114+
s.Spec.TimestampAuthority.Signer = v1alpha1.TimestampAuthoritySigner{
115+
CertificateChain: v1alpha1.CertificateChain{
116+
CertificateChainRef: &v1alpha1.SecretKeySelector{
117+
LocalObjectReference: v1alpha1.LocalObjectReference{
118+
Name: "test-tuf-tsa-secret",
119+
},
120+
Key: "certificateChain",
121+
},
122+
},
123+
File: &v1alpha1.File{
124+
PrivateKeyRef: &v1alpha1.SecretKeySelector{
125+
LocalObjectReference: v1alpha1.LocalObjectReference{
126+
Name: "test-tuf-tsa-secret",
127+
},
128+
Key: "leafPrivateKey",
129+
},
130+
PasswordRef: &v1alpha1.SecretKeySelector{
131+
LocalObjectReference: v1alpha1.LocalObjectReference{
132+
Name: "test-tuf-tsa-secret",
133+
},
134+
Key: "leafPrivateKeyPassword",
135+
},
136+
},
137+
}
138+
return cli.Update(ctx, s)
139+
}).Should(Succeed())
140+
tsa.Verify(ctx, cli, namespace.Name, s.Name)
141+
})
142+
})
143+
})

test/e2e/support/tas/tsa/tsa.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,16 @@ func CreateSecrets(ns string, name string) *v1.Secret {
197197
}
198198
}
199199

200+
func CreateCustomTsaSecret(ns string, name string, data map[string][]byte) *v1.Secret {
201+
return &v1.Secret{
202+
ObjectMeta: metav1.ObjectMeta{
203+
Name: name,
204+
Namespace: ns,
205+
},
206+
Data: data,
207+
}
208+
}
209+
200210
func getCertTemplate(isCA bool) *x509.Certificate {
201211
notBefore := time.Now()
202212
notAfter := notBefore.Add(365 * 24 * 10 * time.Hour)

0 commit comments

Comments
 (0)