blob: d2becf745c1bdf3bad9710cd5d7ec397d90efe66 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Serge Bazanski999e1db2021-11-30 20:37:38 +01004package pki
5
6import (
7 "context"
8 "crypto/x509"
9 "testing"
10
Lorenz Brund13c1c62022-03-30 19:58:58 +020011 "go.etcd.io/etcd/client/pkg/v3/testutil"
12 "go.etcd.io/etcd/tests/v3/integration"
Serge Bazanski999e1db2021-11-30 20:37:38 +010013)
14
15// TestRevoke exercises the CRL revocation and watching functionality of a CA
16// certificate.
17func TestRevoke(t *testing.T) {
Lorenz Brund13c1c62022-03-30 19:58:58 +020018 tb, cancel := testutil.NewTestingTBProthesis("pki-revoke")
19 defer cancel()
20 cluster := integration.NewClusterV3(tb, &integration.ClusterConfig{
Serge Bazanski999e1db2021-11-30 20:37:38 +010021 Size: 1,
22 })
Tim Windelschmidtda1c9502024-05-08 01:24:29 +020023 cl := cluster.Client(0)
Lorenz Brund13c1c62022-03-30 19:58:58 +020024 defer cluster.Terminate(tb)
Serge Bazanski999e1db2021-11-30 20:37:38 +010025 ctx, ctxC := context.WithCancel(context.Background())
26 defer ctxC()
27 ns := Namespaced("/test-managed/")
28
29 ca := &Certificate{
30 Namespace: &ns,
31 Issuer: SelfSigned,
32 Name: "ca",
33 Template: CA("Test CA"),
34 }
35 sub := &Certificate{
36 Namespace: &ns,
37 Issuer: ca,
38 Name: "sub",
39 Template: Server([]string{"server"}, nil),
40 }
41
42 caCertBytes, err := ca.Ensure(ctx, cl)
43 if err != nil {
44 t.Fatalf("Ensuring ca certificate failed: %v", err)
45 }
46 caCert, err := x509.ParseCertificate(caCertBytes)
47 if err != nil {
48 t.Fatalf("Loading newly emitted CA certificate failed: %v", err)
49 }
50
51 subCertBytes, err := sub.Ensure(ctx, cl)
52 if err != nil {
53 t.Fatalf("Ensuring sub certificate failed: %v", err)
54 }
55 subCert, err := x509.ParseCertificate(subCertBytes)
56 if err != nil {
57 t.Fatalf("Loading newly emitted sub certificate failed: %v", err)
58 }
59
60 // Ensure CRL is correctly signed and that subCert is not yet on it.
61 crlW := ca.WatchCRL(cl)
62 crl, err := crlW.Get(ctx)
63 if err != nil {
64 t.Fatalf("Retrieving initial CRL failed: %v", err)
65 }
66 if err := caCert.CheckCRLSignature(crl.List); err != nil {
67 t.Fatalf("Initial CRL not signed by CA: %v", err)
68 }
69 for _, el := range crl.List.TBSCertList.RevokedCertificates {
70 if el.SerialNumber.Cmp(subCert.SerialNumber) == 0 {
71 t.Fatalf("Newly emitted certificate is already on CRL.")
72 }
73 }
74
75 // Emit yet another certificate. Also shouldn't be on CRL.
76 bad := &Certificate{
77 Namespace: &ns,
78 Issuer: ca,
79 Name: "bad",
80 Template: Server([]string{"badserver"}, nil),
81 }
82 badCertBytes, err := bad.Ensure(ctx, cl)
83 if err != nil {
84 t.Fatalf("Ensuring bad certificate failed: %v", err)
85 }
86 badCert, err := x509.ParseCertificate(badCertBytes)
87 if err != nil {
88 t.Fatalf("Loading newly emitted bad certificate failed: %v", err)
89 }
90 for _, el := range crl.List.TBSCertList.RevokedCertificates {
91 if el.SerialNumber.Cmp(badCert.SerialNumber) == 0 {
92 t.Fatalf("Newly emitted bad certificate is already on CRL.")
93 }
94 }
95
96 // Revoke bad certificate. Should now be present in CRL.
97 if err := ca.Revoke(ctx, cl, "badserver"); err != nil {
98 t.Fatalf("Revoke failed: %v", err)
99 }
100 // Get in a loop until found.
101 for {
102 crl, err = crlW.Get(ctx)
103 if err != nil {
104 t.Fatalf("Get failed: %v", err)
105 }
106 found := false
107 for _, el := range crl.List.TBSCertList.RevokedCertificates {
108 if el.SerialNumber.Cmp(badCert.SerialNumber) == 0 {
109 found = true
110 }
111 if el.SerialNumber.Cmp(subCert.SerialNumber) == 0 {
112 t.Errorf("Found non-revoked cert in CRL")
113 }
114 }
115 if found {
116 break
117 }
118 }
119 // Now revoke first certificate. Both should be now present in CRL.
120 if err := ca.Revoke(ctx, cl, "server"); err != nil {
121 t.Fatalf("Revoke failed: %v", err)
122 }
123 // Get in a loop until found.
124 for {
125 crl, err = crlW.Get(ctx)
126 if err != nil {
127 t.Fatalf("Get failed: %v", err)
128 }
129 foundSub := false
130 foundBad := false
131 for _, el := range crl.List.TBSCertList.RevokedCertificates {
132 if el.SerialNumber.Cmp(badCert.SerialNumber) == 0 {
133 foundBad = true
134 }
135 if el.SerialNumber.Cmp(subCert.SerialNumber) == 0 {
136 foundSub = true
137 }
138 }
139 if foundBad && foundSub {
140 break
141 }
142 }
143}