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