1 package in_toto
2
3 import (
4 "crypto"
5 "crypto/ed25519"
6 "crypto/rand"
7 "crypto/rsa"
8 "crypto/x509"
9 "crypto/x509/pkix"
10 "encoding/pem"
11 "fmt"
12 "math/big"
13 "net/url"
14 "testing"
15 "time"
16 )
17
18 type checkConstraintAttributeCase struct {
19 Constraints []string
20 Values []string
21 Expected bool
22 }
23
24 func TestCheckCertConstraint(t *testing.T) {
25 cases := []checkConstraintAttributeCase{
26 {
27 Constraints: []string{"test1", "test2"},
28 Values: []string{"test2", "test1"},
29 Expected: true,
30 },
31 {
32 Constraints: []string{"test1", "test2"},
33 Values: []string{"test2"},
34 Expected: false,
35 },
36 {
37 Constraints: []string{AllowAllConstraint},
38 Values: []string{"any", "thing", "goes"},
39 Expected: true,
40 },
41 {
42 Constraints: []string{},
43 Values: []string{},
44 Expected: true,
45 },
46 {
47 Constraints: []string{},
48 Values: []string{"test1"},
49 Expected: false,
50 },
51 {
52 Constraints: []string{""},
53 Values: []string{""},
54 Expected: true,
55 },
56 {
57 Constraints: []string{""},
58 Values: []string{"test1"},
59 Expected: false,
60 },
61 {
62 Constraints: []string{"test1", "test2"},
63 Values: []string{"test1", "test2", "test3"},
64 Expected: false,
65 },
66 }
67
68 for _, c := range cases {
69 err := checkCertConstraint("constraint", c.Constraints, c.Values)
70 actual := err == nil
71 if actual != c.Expected {
72 t.Errorf("got %v when expected %v. Constraints: %v, Values: %v", actual, c.Expected, c.Constraints, c.Values)
73 }
74 }
75 }
76
77 type constraintCheckCase struct {
78 Constraint CertificateConstraint
79 Cert *x509.Certificate
80 Expected bool
81 }
82
83 func TestConstraintCheck(t *testing.T) {
84 testCertSubject := pkix.Name{
85 CommonName: "step1.example.com",
86 Organization: []string{"example"},
87 }
88 testCertEmails := []string{"example@example.com"}
89 testCertDNSNames := []string{"example.com"}
90 testCertURI, _ := url.Parse("spiffe://example.com/step1")
91 testCertURIs := []*url.URL{testCertURI}
92 testertValidity := 1 * time.Hour
93 testCertPublicKeyAlgorithm := x509.Ed25519
94 testCertTemplate := &x509.Certificate{
95 Subject: testCertSubject,
96 EmailAddresses: testCertEmails,
97 DNSNames: testCertDNSNames,
98 URIs: testCertURIs,
99 }
100
101 testCert, testIntermediateCert, testRootCert, err := createTestCert(testCertTemplate, testCertPublicKeyAlgorithm, testertValidity)
102 if err != nil {
103 t.Fatalf("failed to create test cert: %v", err)
104 }
105
106 rootCertPool := x509.NewCertPool()
107 rootCertPool.AddCert(testRootCert)
108 intermediateCertPool := x509.NewCertPool()
109 intermediateCertPool.AddCert(testIntermediateCert)
110
111 roots := []string{"example"}
112
113 cases := []constraintCheckCase{
114 {
115 Cert: testCert,
116 Constraint: CertificateConstraint{
117 CommonName: "step1.example.com",
118 DNSNames: []string{"example.com"},
119 Emails: []string{"example@example.com"},
120 Organizations: []string{"example"},
121 Roots: []string{"example"},
122 URIs: []string{"spiffe://example.com/step1"},
123 },
124 Expected: true,
125 },
126 {
127 Cert: testCert,
128 Constraint: CertificateConstraint{
129 CommonName: "*",
130 DNSNames: []string{"*"},
131 Emails: []string{"*"},
132 Organizations: []string{"*"},
133 Roots: []string{"*"},
134 URIs: []string{"*"},
135 },
136 Expected: true,
137 },
138 {
139 Cert: testCert,
140 Constraint: CertificateConstraint{
141 CommonName: "",
142 DNSNames: []string{},
143 Emails: []string{},
144 Organizations: []string{},
145 Roots: []string{},
146 URIs: []string{},
147 },
148 Expected: false,
149 },
150 {
151 Cert: testCert,
152 Constraint: CertificateConstraint{
153 CommonName: "",
154 DNSNames: []string{""},
155 Emails: []string{""},
156 Organizations: []string{""},
157 Roots: []string{""},
158 URIs: []string{""},
159 },
160 Expected: false,
161 },
162 {
163 Cert: testCert,
164 Constraint: CertificateConstraint{
165 CommonName: "",
166 DNSNames: []string{"example.com"},
167 Emails: []string{"example@example.com"},
168 Organizations: []string{"example"},
169 Roots: []string{"example"},
170 URIs: []string{"spiffe://example.com/step1"},
171 },
172 Expected: false,
173 },
174 {
175 Cert: testCert,
176 Constraint: CertificateConstraint{
177 CommonName: "step1.example.com",
178 DNSNames: []string{},
179 Emails: []string{"example@example.com"},
180 Organizations: []string{"example"},
181 Roots: []string{"example"},
182 URIs: []string{"spiffe://example.com/step1"},
183 },
184 Expected: false,
185 },
186 {
187 Cert: testCert,
188 Constraint: CertificateConstraint{
189 CommonName: "step1.example.com",
190 DNSNames: []string{"example.com"},
191 Emails: []string{},
192 Organizations: []string{"example"},
193 Roots: []string{"example"},
194 URIs: []string{"spiffe://example.com/step1"},
195 },
196 Expected: false,
197 },
198 {
199 Cert: testCert,
200 Constraint: CertificateConstraint{
201 CommonName: "step1.example.com",
202 DNSNames: []string{"example.com"},
203 Emails: []string{"example@example.com"},
204 Organizations: []string{},
205 Roots: []string{"example"},
206 URIs: []string{"spiffe://example.com/step1"},
207 },
208 Expected: false,
209 },
210 {
211 Cert: testCert,
212 Constraint: CertificateConstraint{
213 CommonName: "step1.example.com",
214 DNSNames: []string{"example.com"},
215 Emails: []string{"example@example.com"},
216 Organizations: []string{"example"},
217 Roots: []string{},
218 URIs: []string{"spiffe://example.com/step1"},
219 },
220 Expected: false,
221 },
222 {
223 Cert: testCert,
224 Constraint: CertificateConstraint{
225 CommonName: "*",
226 DNSNames: []string{"*"},
227 Emails: []string{"*"},
228 Organizations: []string{"*"},
229 Roots: []string{"example2"},
230 URIs: []string{"*"},
231 },
232 Expected: false,
233 },
234 {
235 Cert: testCert,
236 Constraint: CertificateConstraint{
237 CommonName: "step1.example.com",
238 DNSNames: []string{"example.com"},
239 Emails: []string{"example@example.com"},
240 Organizations: []string{"example"},
241 Roots: []string{"example"},
242 URIs: []string{},
243 },
244 Expected: false,
245 },
246 {
247 Cert: testCert,
248 Constraint: CertificateConstraint{
249 CommonName: "*",
250 DNSNames: []string{"*"},
251 Emails: []string{"*"},
252 Organizations: []string{"*"},
253 Roots: []string{"*"},
254 URIs: []string{"spiffe://example.com/step1", "step1.example.com"},
255 },
256 Expected: false,
257 },
258 {
259 Cert: testCert,
260 Constraint: CertificateConstraint{
261 CommonName: "*",
262 DNSNames: []string{"*"},
263 Emails: []string{"*"},
264 Organizations: []string{"*"},
265 Roots: []string{"*"},
266 URIs: []string{"spiffe://example.com/step2"},
267 },
268 Expected: false,
269 },
270 }
271
272 for _, c := range cases {
273 err := c.Constraint.Check(c.Cert, roots, rootCertPool, intermediateCertPool)
274 actual := err == nil
275 if actual != c.Expected {
276 t.Errorf("got %v when expected %v. Constraint: %+v, Errors: %s", actual, c.Expected, c.Constraint, err)
277 }
278 }
279 }
280
281 func createTestCert(template *x509.Certificate, publicKeyAlgorithm x509.PublicKeyAlgorithm, validity time.Duration) (*x509.Certificate, *x509.Certificate, *x509.Certificate, error) {
282 rootCertSubject := pkix.Name{
283 CommonName: "Root CA",
284 }
285 rootCertMaxPathLen := 1
286 rootCertValidity := 10 * 365 * 24 * time.Hour
287 rootCertPublicKeyAlgorithm := x509.Ed25519
288 rootCertTemplate := &x509.Certificate{
289 Subject: rootCertSubject,
290 MaxPathLen: rootCertMaxPathLen,
291 }
292 rootCert, _, rootKey, err := createSelfSignedCA(rootCertTemplate, rootCertPublicKeyAlgorithm, rootCertValidity)
293 if err != nil {
294 return nil, nil, nil, err
295 }
296
297 intermediateCertSubject := pkix.Name{
298 CommonName: "Intermediate CA",
299 }
300 intermediateCertMaxPathLen := 0
301 intermediateCertValidity := 10 * 365 * 24 * time.Hour
302 intermediateCertPublicKeyAlgorithm := x509.Ed25519
303 intermediateCertTemplate := &x509.Certificate{
304 Subject: intermediateCertSubject,
305 MaxPathLen: intermediateCertMaxPathLen,
306 }
307 intermediateCert, _, intermediateKey, err := createCA(intermediateCertTemplate, rootCert, rootKey, intermediateCertPublicKeyAlgorithm, intermediateCertValidity)
308 if err != nil {
309 return nil, nil, nil, err
310 }
311
312 endEntityCert, _, _, err := createEndEntityCert(template, intermediateCert, intermediateKey, publicKeyAlgorithm, validity)
313 if err != nil {
314 return nil, nil, nil, err
315 }
316
317 return endEntityCert, intermediateCert, rootCert, nil
318 }
319
320 func createSelfSignedCA(template *x509.Certificate, publicKeyAlgorithm x509.PublicKeyAlgorithm, validity time.Duration) (*x509.Certificate, []byte, crypto.PrivateKey, error) {
321 if template.Subject.CommonName == "" {
322 return nil, nil, nil, fmt.Errorf("subject common name must be set")
323 }
324
325 if template.MaxPathLen <= 0 {
326 return nil, nil, nil, fmt.Errorf("maxPathLen must be set and greater than 0")
327 }
328
329 serialNumber, err := createSerialNumber()
330 if err != nil {
331 return nil, nil, nil, fmt.Errorf("failed to create certificate serial number: %w", err)
332 }
333
334 template.SerialNumber = serialNumber
335 template.NotBefore = time.Now()
336 template.NotAfter = time.Now().Add(validity)
337 template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature
338 template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
339 template.BasicConstraintsValid = true
340 template.IsCA = true
341 template.MaxPathLenZero = false
342
343 publicKey, privateKey, err := createKeyPair(publicKeyAlgorithm)
344 if err != nil {
345 return nil, nil, nil, fmt.Errorf("failed to create key pair: %w", err)
346 }
347
348 cert, certPEM, err := createCert(template, template, publicKey, privateKey)
349 if err != nil {
350 return nil, nil, nil, fmt.Errorf("failed to create cert: %w", err)
351 }
352
353 return cert, certPEM, privateKey, nil
354 }
355
356 func createCA(template, issuerCert *x509.Certificate, issuerPrivateKey crypto.PrivateKey, publicKeyAlgorithm x509.PublicKeyAlgorithm, validity time.Duration) (*x509.Certificate, []byte, crypto.PrivateKey, error) {
357 if template.Subject.CommonName == "" {
358 return nil, nil, nil, fmt.Errorf("subject common name must be set")
359 }
360
361 serialNumber, err := createSerialNumber()
362 if err != nil {
363 return nil, nil, nil, fmt.Errorf("failed to create certificate serial number: %w", err)
364 }
365
366 var maxPathLenZero bool
367 if template.MaxPathLen > 0 {
368 maxPathLenZero = false
369 } else {
370 maxPathLenZero = true
371 }
372
373 template.SerialNumber = serialNumber
374 template.NotBefore = time.Now()
375 template.NotAfter = time.Now().Add(validity)
376 template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature
377 template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
378 template.BasicConstraintsValid = true
379 template.IsCA = true
380 template.MaxPathLenZero = maxPathLenZero
381
382 publicKey, privateKey, err := createKeyPair(publicKeyAlgorithm)
383 if err != nil {
384 return nil, nil, nil, fmt.Errorf("failed to create key pair: %w", err)
385 }
386
387 cert, certPEM, err := createCert(template, issuerCert, publicKey, issuerPrivateKey)
388 if err != nil {
389 return nil, nil, nil, fmt.Errorf("failed to create cert: %w", err)
390 }
391
392 return cert, certPEM, privateKey, nil
393 }
394
395 func createEndEntityCert(template, issuerCert *x509.Certificate, issuerPrivateKey crypto.PrivateKey, publicKeyAlgorithm x509.PublicKeyAlgorithm, validity time.Duration) (*x509.Certificate, []byte, crypto.PrivateKey, error) {
396 if template.Subject.CommonName == "" {
397 return nil, nil, nil, fmt.Errorf("subject common name must be set")
398 }
399
400 serialNumber, err := createSerialNumber()
401 if err != nil {
402 return nil, nil, nil, fmt.Errorf("failed to create certificate serial number: %w", err)
403 }
404
405 template.SerialNumber = serialNumber
406 template.NotBefore = time.Now()
407 template.NotAfter = time.Now().Add(validity)
408 template.KeyUsage = x509.KeyUsageDigitalSignature
409 template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
410 template.IsCA = false
411 template.MaxPathLenZero = true
412
413 publicKey, privateKey, err := createKeyPair(publicKeyAlgorithm)
414 if err != nil {
415 return nil, nil, nil, fmt.Errorf("failed to create key pair: %w", err)
416 }
417
418 cert, certPEM, err := createCert(template, issuerCert, publicKey, issuerPrivateKey)
419 if err != nil {
420 return nil, nil, nil, fmt.Errorf("failed to create cert: %w", err)
421 }
422
423 return cert, certPEM, privateKey, nil
424 }
425
426 func createKeyPair(publicKeyAlgorithm x509.PublicKeyAlgorithm) (crypto.PublicKey, crypto.PrivateKey, error) {
427 var publicKey crypto.PublicKey
428 var privateKey crypto.PrivateKey
429
430 switch publicKeyAlgorithm {
431
432 case x509.RSA:
433 rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
434 if err != nil {
435 return nil, nil, fmt.Errorf("failed to generate rsa private key: %w", err)
436 }
437 publicKey = &rsaPrivateKey.PublicKey
438 privateKey = rsaPrivateKey
439
440 case x509.Ed25519:
441 ed25519PublicKey, ed25519PrivateKey, err := ed25519.GenerateKey(rand.Reader)
442 if err != nil {
443 return nil, nil, fmt.Errorf("failed to generate ed25519 key pair: %w", err)
444 }
445 publicKey = ed25519PublicKey
446 privateKey = ed25519PrivateKey
447
448 default:
449 return nil, nil, fmt.Errorf("unsupported public key algorithm")
450 }
451
452 return publicKey, privateKey, nil
453 }
454
455 func createCert(template, issuer *x509.Certificate, subjectPublicKey crypto.PublicKey, issuerPrivateKey crypto.PrivateKey) (*x509.Certificate, []byte, error) {
456 certBytes, err := x509.CreateCertificate(rand.Reader, template, issuer, subjectPublicKey, issuerPrivateKey)
457 if err != nil {
458 return nil, nil, fmt.Errorf("failed to create certificate: %w", err)
459 }
460
461 cert, err := x509.ParseCertificate(certBytes)
462 if err != nil {
463 return nil, nil, fmt.Errorf("failed to parse certificate: %w", err)
464 }
465
466 b := pem.Block{Type: "CERTIFICATE", Bytes: certBytes}
467 certPEM := pem.EncodeToMemory(&b)
468
469 return cert, certPEM, err
470 }
471
472 func createSerialNumber() (*big.Int, error) {
473 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 256)
474 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
475 if err != nil {
476 return nil, fmt.Errorf("failed to generate serial number: %w", err)
477 }
478 return serialNumber, nil
479 }
480
View as plain text