1 package test
2
3 import (
4 "bytes"
5 "crypto"
6 "crypto/ecdsa"
7 "crypto/elliptic"
8 "crypto/rand"
9 "crypto/x509"
10 "crypto/x509/pkix"
11 "fmt"
12 "math/big"
13 "net/url"
14 "testing"
15 "time"
16
17 "github.com/spiffe/go-spiffe/v2/bundle/spiffebundle"
18 "github.com/spiffe/go-spiffe/v2/bundle/x509bundle"
19 "github.com/spiffe/go-spiffe/v2/spiffeid"
20 "github.com/spiffe/go-spiffe/v2/svid/x509svid"
21 "github.com/stretchr/testify/require"
22 )
23
24 type CA struct {
25 tb testing.TB
26 td spiffeid.TrustDomain
27 parent *CA
28 cert *x509.Certificate
29 key crypto.Signer
30 jwtKey crypto.Signer
31 jwtKid string
32 }
33
34 type CertificateOption interface {
35 apply(*x509.Certificate)
36 }
37
38 type certificateOption func(*x509.Certificate)
39
40 func (co certificateOption) apply(c *x509.Certificate) {
41 co(c)
42 }
43
44
45 func NewEC256Key(tb testing.TB) *ecdsa.PrivateKey {
46 key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
47 require.NoError(tb, err)
48 return key
49 }
50
51
52 func NewKeyID(tb testing.TB) string {
53 choices := make([]byte, 32)
54 _, err := rand.Read(choices)
55 require.NoError(tb, err)
56 return keyIDFromBytes(choices)
57 }
58
59 func keyIDFromBytes(choices []byte) string {
60 const alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
61 buf := new(bytes.Buffer)
62 for _, choice := range choices {
63 buf.WriteByte(alphabet[int(choice)%len(alphabet)])
64 }
65 return buf.String()
66 }
67
68 func NewCA(tb testing.TB, td spiffeid.TrustDomain) *CA {
69 cert, key := CreateCACertificate(tb, nil, nil)
70 return &CA{
71 tb: tb,
72 td: td,
73 cert: cert,
74 key: key,
75 jwtKey: NewEC256Key(tb),
76 jwtKid: NewKeyID(tb),
77 }
78 }
79
80 func (ca *CA) ChildCA(options ...CertificateOption) *CA {
81 cert, key := CreateCACertificate(ca.tb, ca.cert, ca.key, options...)
82 return &CA{
83 tb: ca.tb,
84 parent: ca,
85 cert: cert,
86 key: key,
87 jwtKey: NewEC256Key(ca.tb),
88 jwtKid: NewKeyID(ca.tb),
89 }
90 }
91
92 func (ca *CA) CreateX509SVID(id spiffeid.ID, options ...CertificateOption) *x509svid.SVID {
93 cert, key := CreateX509SVID(ca.tb, ca.cert, ca.key, id, options...)
94 return &x509svid.SVID{
95 ID: id,
96 Certificates: append([]*x509.Certificate{cert}, ca.chain(false)...),
97 PrivateKey: key,
98 }
99 }
100
101 func (ca *CA) CreateX509SVIDNoPrivateKey(id spiffeid.ID, options ...CertificateOption) *x509svid.SVID {
102 cert, _ := CreateX509SVID(ca.tb, ca.cert, ca.key, id, options...)
103 return &x509svid.SVID{
104 ID: id,
105 Certificates: append([]*x509.Certificate{cert}, ca.chain(false)...),
106 }
107 }
108
109 func (ca *CA) CreateX509Certificate(options ...CertificateOption) ([]*x509.Certificate, crypto.Signer) {
110 cert, key := CreateX509Certificate(ca.tb, ca.cert, ca.key, options...)
111 return append([]*x509.Certificate{cert}, ca.chain(false)...), key
112 }
113
114 func (ca *CA) X509Authorities() []*x509.Certificate {
115 root := ca
116 for root.parent != nil {
117 root = root.parent
118 }
119 return []*x509.Certificate{root.cert}
120 }
121
122 func (ca *CA) Bundle() *spiffebundle.Bundle {
123 bundle := spiffebundle.New(ca.td)
124 bundle.SetX509Authorities(ca.X509Authorities())
125 return bundle
126 }
127
128 func (ca *CA) X509Bundle() *x509bundle.Bundle {
129 return x509bundle.FromX509Authorities(ca.td, ca.X509Authorities())
130 }
131
132 func CreateCACertificate(tb testing.TB, parent *x509.Certificate, parentKey crypto.Signer, options ...CertificateOption) (*x509.Certificate, crypto.Signer) {
133 now := time.Now()
134 serial := NewSerial(tb)
135 key := NewEC256Key(tb)
136 tmpl := &x509.Certificate{
137 SerialNumber: serial,
138 Subject: pkix.Name{
139 CommonName: fmt.Sprintf("CA %x", serial),
140 },
141 BasicConstraintsValid: true,
142 IsCA: true,
143 NotBefore: now,
144 NotAfter: now.Add(time.Hour),
145 }
146
147 applyOptions(tmpl, options...)
148
149 if parent == nil {
150 parent = tmpl
151 parentKey = key
152 }
153 return CreateCertificate(tb, tmpl, parent, key.Public(), parentKey), key
154 }
155
156 func CreateX509Certificate(tb testing.TB, parent *x509.Certificate, parentKey crypto.Signer, options ...CertificateOption) (*x509.Certificate, crypto.Signer) {
157 now := time.Now()
158 serial := NewSerial(tb)
159 key := NewEC256Key(tb)
160 tmpl := &x509.Certificate{
161 SerialNumber: serial,
162 Subject: pkix.Name{
163 CommonName: fmt.Sprintf("X509-Certificate %x", serial),
164 },
165 NotBefore: now,
166 NotAfter: now.Add(time.Hour),
167 KeyUsage: x509.KeyUsageDigitalSignature,
168 }
169
170 applyOptions(tmpl, options...)
171
172 return CreateCertificate(tb, tmpl, parent, key.Public(), parentKey), key
173 }
174
175 func CreateX509SVID(tb testing.TB, parent *x509.Certificate, parentKey crypto.Signer, id spiffeid.ID, options ...CertificateOption) (*x509.Certificate, crypto.Signer) {
176 serial := NewSerial(tb)
177 options = append(options,
178 WithSerial(serial),
179 WithKeyUsage(x509.KeyUsageDigitalSignature),
180 WithSubject(pkix.Name{
181 CommonName: fmt.Sprintf("X509-SVID %x", serial),
182 }),
183 WithURIs(id.URL()))
184
185 return CreateX509Certificate(tb, parent, parentKey, options...)
186 }
187
188 func CreateCertificate(tb testing.TB, tmpl, parent *x509.Certificate, pub, priv interface{}) *x509.Certificate {
189 certDER, err := x509.CreateCertificate(rand.Reader, tmpl, parent, pub, priv)
190 require.NoError(tb, err)
191 cert, err := x509.ParseCertificate(certDER)
192 require.NoError(tb, err)
193 return cert
194 }
195
196 func NewSerial(tb testing.TB) *big.Int {
197 b := make([]byte, 8)
198 _, err := rand.Read(b)
199 require.NoError(tb, err)
200 return new(big.Int).SetBytes(b)
201 }
202
203 func WithSerial(serial *big.Int) CertificateOption {
204 return certificateOption(func(c *x509.Certificate) {
205 c.SerialNumber = serial
206 })
207 }
208
209 func WithKeyUsage(keyUsage x509.KeyUsage) CertificateOption {
210 return certificateOption(func(c *x509.Certificate) {
211 c.KeyUsage = keyUsage
212 })
213 }
214
215 func WithURIs(uris ...*url.URL) CertificateOption {
216 return certificateOption(func(c *x509.Certificate) {
217 c.URIs = uris
218 })
219 }
220
221 func WithSubject(subject pkix.Name) CertificateOption {
222 return certificateOption(func(c *x509.Certificate) {
223 c.Subject = subject
224 })
225 }
226
227 func applyOptions(c *x509.Certificate, options ...CertificateOption) {
228 for _, opt := range options {
229 opt.apply(c)
230 }
231 }
232
233 func (ca *CA) chain(includeRoot bool) []*x509.Certificate {
234 chain := []*x509.Certificate{}
235 next := ca
236 for next != nil {
237 if includeRoot || next.parent != nil {
238 chain = append(chain, next.cert)
239 }
240 next = next.parent
241 }
242 return chain
243 }
244
View as plain text