github.com/letsencrypt/boulder@v0.20251208.0/cmd/ceremony/main_test.go (about) 1 package main 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/elliptic" 6 "crypto/rand" 7 "crypto/x509" 8 "encoding/pem" 9 "fmt" 10 "io/fs" 11 "math/big" 12 "os" 13 "path" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/jmhodges/clock" 19 20 "github.com/letsencrypt/boulder/test" 21 ) 22 23 func TestLoadPubKey(t *testing.T) { 24 tmp := t.TempDir() 25 key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 26 27 _, _, err := loadPubKey(path.Join(tmp, "does", "not", "exist")) 28 test.AssertError(t, err, "should fail on non-existent file") 29 test.AssertErrorIs(t, err, fs.ErrNotExist) 30 31 _, _, err = loadPubKey("../../test/hierarchy/README.md") 32 test.AssertError(t, err, "should fail on non-PEM file") 33 34 priv, _ := x509.MarshalPKCS8PrivateKey(key) 35 _ = os.WriteFile(path.Join(tmp, "priv.pem"), pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: priv}), 0644) 36 _, _, err = loadPubKey(path.Join(tmp, "priv.pem")) 37 test.AssertError(t, err, "should fail on non-pubkey PEM") 38 39 pub, _ := x509.MarshalPKIXPublicKey(key.Public()) 40 _ = os.WriteFile(path.Join(tmp, "pub.pem"), pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pub}), 0644) 41 _, _, err = loadPubKey(path.Join(tmp, "pub.pem")) 42 test.AssertNotError(t, err, "should not have errored") 43 } 44 45 func TestCheckOutputFileSucceeds(t *testing.T) { 46 dir := t.TempDir() 47 err := checkOutputFile(dir+"/example", "foo") 48 if err != nil { 49 t.Fatal(err) 50 } 51 } 52 53 func TestCheckOutputFileEmpty(t *testing.T) { 54 err := checkOutputFile("", "foo") 55 if err == nil { 56 t.Fatal("expected error, got none") 57 } 58 if err.Error() != "outputs.foo is required" { 59 t.Fatalf("wrong error: %s", err) 60 } 61 } 62 63 func TestCheckOutputFileExists(t *testing.T) { 64 dir := t.TempDir() 65 filename := dir + "/example" 66 err := writeFile(filename, []byte("hi")) 67 if err != nil { 68 t.Fatal(err) 69 } 70 err = checkOutputFile(filename, "foo") 71 if err == nil { 72 t.Fatal("expected error, got none") 73 } 74 if !strings.Contains(err.Error(), "already exists") { 75 t.Fatalf("wrong error: %s", err) 76 } 77 } 78 79 func TestKeyGenConfigValidate(t *testing.T) { 80 cases := []struct { 81 name string 82 config keyGenConfig 83 expectedError string 84 }{ 85 { 86 name: "no key.type", 87 config: keyGenConfig{}, 88 expectedError: "key.type is required", 89 }, 90 { 91 name: "bad key.type", 92 config: keyGenConfig{ 93 Type: "doop", 94 }, 95 expectedError: "key.type can only be 'rsa' or 'ecdsa'", 96 }, 97 { 98 name: "bad key.rsa-mod-length", 99 config: keyGenConfig{ 100 Type: "rsa", 101 RSAModLength: 1337, 102 }, 103 expectedError: "key.rsa-mod-length can only be 2048 or 4096", 104 }, 105 { 106 name: "key.type is rsa but key.ecdsa-curve is present", 107 config: keyGenConfig{ 108 Type: "rsa", 109 RSAModLength: 2048, 110 ECDSACurve: "bad", 111 }, 112 expectedError: "if key.type = 'rsa' then key.ecdsa-curve is not used", 113 }, 114 { 115 name: "bad key.ecdsa-curve", 116 config: keyGenConfig{ 117 Type: "ecdsa", 118 ECDSACurve: "bad", 119 }, 120 expectedError: "key.ecdsa-curve can only be 'P-256', 'P-384', or 'P-521'", 121 }, 122 { 123 name: "key.type is ecdsa but key.rsa-mod-length is present", 124 config: keyGenConfig{ 125 Type: "ecdsa", 126 RSAModLength: 2048, 127 ECDSACurve: "P-256", 128 }, 129 expectedError: "if key.type = 'ecdsa' then key.rsa-mod-length is not used", 130 }, 131 { 132 name: "good rsa config", 133 config: keyGenConfig{ 134 Type: "rsa", 135 RSAModLength: 2048, 136 }, 137 }, 138 { 139 name: "good ecdsa config", 140 config: keyGenConfig{ 141 Type: "ecdsa", 142 ECDSACurve: "P-256", 143 }, 144 }, 145 } 146 for _, tc := range cases { 147 t.Run(tc.name, func(t *testing.T) { 148 err := tc.config.validate() 149 if err != nil && err.Error() != tc.expectedError { 150 t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err) 151 } else if err == nil && tc.expectedError != "" { 152 t.Fatalf("validate didn't fail, wanted: %q", err) 153 } 154 }) 155 } 156 } 157 158 func TestRootConfigValidate(t *testing.T) { 159 cases := []struct { 160 name string 161 config rootConfig 162 expectedError string 163 }{ 164 { 165 name: "no pkcs11.module", 166 config: rootConfig{}, 167 expectedError: "pkcs11.module is required", 168 }, 169 { 170 name: "no pkcs11.store-key-with-label", 171 config: rootConfig{ 172 PKCS11: PKCS11KeyGenConfig{ 173 Module: "module", 174 }, 175 }, 176 expectedError: "pkcs11.store-key-with-label is required", 177 }, 178 { 179 name: "bad key fields", 180 config: rootConfig{ 181 PKCS11: PKCS11KeyGenConfig{ 182 Module: "module", 183 StoreLabel: "label", 184 }, 185 }, 186 expectedError: "key.type is required", 187 }, 188 { 189 name: "no outputs.public-key-path", 190 config: rootConfig{ 191 PKCS11: PKCS11KeyGenConfig{ 192 Module: "module", 193 StoreLabel: "label", 194 }, 195 Key: keyGenConfig{ 196 Type: "rsa", 197 RSAModLength: 2048, 198 }, 199 }, 200 expectedError: "outputs.public-key-path is required", 201 }, 202 { 203 name: "no outputs.certificate-path", 204 config: rootConfig{ 205 PKCS11: PKCS11KeyGenConfig{ 206 Module: "module", 207 StoreLabel: "label", 208 }, 209 Key: keyGenConfig{ 210 Type: "rsa", 211 RSAModLength: 2048, 212 }, 213 Outputs: struct { 214 PublicKeyPath string `yaml:"public-key-path"` 215 CertificatePath string `yaml:"certificate-path"` 216 }{ 217 PublicKeyPath: "path", 218 }, 219 }, 220 expectedError: "outputs.certificate-path is required", 221 }, 222 { 223 name: "bad certificate-profile", 224 config: rootConfig{ 225 PKCS11: PKCS11KeyGenConfig{ 226 Module: "module", 227 StoreLabel: "label", 228 }, 229 Key: keyGenConfig{ 230 Type: "rsa", 231 RSAModLength: 2048, 232 }, 233 Outputs: struct { 234 PublicKeyPath string `yaml:"public-key-path"` 235 CertificatePath string `yaml:"certificate-path"` 236 }{ 237 PublicKeyPath: "path", 238 CertificatePath: "path", 239 }, 240 }, 241 expectedError: "not-before is required", 242 }, 243 { 244 name: "good config", 245 config: rootConfig{ 246 PKCS11: PKCS11KeyGenConfig{ 247 Module: "module", 248 StoreLabel: "label", 249 }, 250 Key: keyGenConfig{ 251 Type: "rsa", 252 RSAModLength: 2048, 253 }, 254 Outputs: struct { 255 PublicKeyPath string `yaml:"public-key-path"` 256 CertificatePath string `yaml:"certificate-path"` 257 }{ 258 PublicKeyPath: "path", 259 CertificatePath: "path", 260 }, 261 CertProfile: certProfile{ 262 NotBefore: "a", 263 NotAfter: "b", 264 SignatureAlgorithm: "c", 265 CommonName: "d", 266 Organization: "e", 267 Country: "f", 268 }, 269 SkipLints: []string{ 270 "e_ext_authority_key_identifier_missing", 271 "e_ext_authority_key_identifier_no_key_identifier", 272 "e_sub_ca_aia_missing", 273 "e_sub_ca_certificate_policies_missing", 274 "e_sub_ca_crl_distribution_points_missing", 275 "n_ca_digital_signature_not_set", 276 "n_mp_allowed_eku", 277 "n_sub_ca_eku_missing", 278 "w_sub_ca_aia_does_not_contain_issuing_ca_url", 279 }, 280 }, 281 }, 282 } 283 for _, tc := range cases { 284 t.Run(tc.name, func(t *testing.T) { 285 err := tc.config.validate() 286 if err != nil && err.Error() != tc.expectedError { 287 t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err) 288 } else if err == nil && tc.expectedError != "" { 289 t.Fatalf("validate didn't fail, wanted: %q", err) 290 } 291 }) 292 } 293 } 294 295 func TestIntermediateConfigValidate(t *testing.T) { 296 cases := []struct { 297 name string 298 config intermediateConfig 299 expectedError string 300 }{ 301 { 302 name: "no pkcs11.module", 303 config: intermediateConfig{}, 304 expectedError: "pkcs11.module is required", 305 }, 306 { 307 name: "no pkcs11.signing-key-label", 308 config: intermediateConfig{ 309 PKCS11: PKCS11SigningConfig{ 310 Module: "module", 311 }, 312 }, 313 expectedError: "pkcs11.signing-key-label is required", 314 }, 315 { 316 name: "no inputs.public-key-path", 317 config: intermediateConfig{ 318 PKCS11: PKCS11SigningConfig{ 319 Module: "module", 320 SigningLabel: "label", 321 }, 322 }, 323 expectedError: "inputs.public-key-path is required", 324 }, 325 { 326 name: "no inputs.issuer-certificate-path", 327 config: intermediateConfig{ 328 PKCS11: PKCS11SigningConfig{ 329 Module: "module", 330 SigningLabel: "label", 331 }, 332 Inputs: struct { 333 PublicKeyPath string `yaml:"public-key-path"` 334 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 335 }{ 336 PublicKeyPath: "path", 337 }, 338 }, 339 expectedError: "inputs.issuer-certificate is required", 340 }, 341 { 342 name: "no outputs.certificate-path", 343 config: intermediateConfig{ 344 PKCS11: PKCS11SigningConfig{ 345 Module: "module", 346 SigningLabel: "label", 347 }, 348 Inputs: struct { 349 PublicKeyPath string `yaml:"public-key-path"` 350 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 351 }{ 352 PublicKeyPath: "path", 353 IssuerCertificatePath: "path", 354 }, 355 }, 356 expectedError: "outputs.certificate-path is required", 357 }, 358 { 359 name: "bad certificate-profile", 360 config: intermediateConfig{ 361 PKCS11: PKCS11SigningConfig{ 362 Module: "module", 363 SigningLabel: "label", 364 }, 365 Inputs: struct { 366 PublicKeyPath string `yaml:"public-key-path"` 367 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 368 }{ 369 PublicKeyPath: "path", 370 IssuerCertificatePath: "path", 371 }, 372 Outputs: struct { 373 CertificatePath string `yaml:"certificate-path"` 374 }{ 375 CertificatePath: "path", 376 }, 377 }, 378 expectedError: "not-before is required", 379 }, 380 { 381 name: "too many policy OIDs", 382 config: intermediateConfig{ 383 PKCS11: PKCS11SigningConfig{ 384 Module: "module", 385 SigningLabel: "label", 386 }, 387 Inputs: struct { 388 PublicKeyPath string `yaml:"public-key-path"` 389 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 390 }{ 391 PublicKeyPath: "path", 392 IssuerCertificatePath: "path", 393 }, 394 Outputs: struct { 395 CertificatePath string `yaml:"certificate-path"` 396 }{ 397 CertificatePath: "path", 398 }, 399 CertProfile: certProfile{ 400 NotBefore: "a", 401 NotAfter: "b", 402 SignatureAlgorithm: "c", 403 CommonName: "d", 404 Organization: "e", 405 Country: "f", 406 CRLURL: "h", 407 IssuerURL: "i", 408 Policies: []policyInfoConfig{{OID: "2.23.140.1.2.1"}, {OID: "6.6.6"}}, 409 }, 410 SkipLints: []string{}, 411 }, 412 expectedError: "policy should be exactly BRs domain-validated for subordinate CAs", 413 }, 414 { 415 name: "too few policy OIDs", 416 config: intermediateConfig{ 417 PKCS11: PKCS11SigningConfig{ 418 Module: "module", 419 SigningLabel: "label", 420 }, 421 Inputs: struct { 422 PublicKeyPath string `yaml:"public-key-path"` 423 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 424 }{ 425 PublicKeyPath: "path", 426 IssuerCertificatePath: "path", 427 }, 428 Outputs: struct { 429 CertificatePath string `yaml:"certificate-path"` 430 }{ 431 CertificatePath: "path", 432 }, 433 CertProfile: certProfile{ 434 NotBefore: "a", 435 NotAfter: "b", 436 SignatureAlgorithm: "c", 437 CommonName: "d", 438 Organization: "e", 439 Country: "f", 440 CRLURL: "h", 441 IssuerURL: "i", 442 Policies: []policyInfoConfig{}, 443 }, 444 SkipLints: []string{}, 445 }, 446 expectedError: "policy should be exactly BRs domain-validated for subordinate CAs", 447 }, 448 { 449 name: "good config", 450 config: intermediateConfig{ 451 PKCS11: PKCS11SigningConfig{ 452 Module: "module", 453 SigningLabel: "label", 454 }, 455 Inputs: struct { 456 PublicKeyPath string `yaml:"public-key-path"` 457 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 458 }{ 459 PublicKeyPath: "path", 460 IssuerCertificatePath: "path", 461 }, 462 Outputs: struct { 463 CertificatePath string `yaml:"certificate-path"` 464 }{ 465 CertificatePath: "path", 466 }, 467 CertProfile: certProfile{ 468 NotBefore: "a", 469 NotAfter: "b", 470 SignatureAlgorithm: "c", 471 CommonName: "d", 472 Organization: "e", 473 Country: "f", 474 CRLURL: "h", 475 IssuerURL: "i", 476 Policies: []policyInfoConfig{{OID: "2.23.140.1.2.1"}}, 477 }, 478 SkipLints: []string{}, 479 }, 480 }, 481 } 482 for _, tc := range cases { 483 t.Run(tc.name, func(t *testing.T) { 484 err := tc.config.validate() 485 if err != nil && err.Error() != tc.expectedError { 486 t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err) 487 } else if err == nil && tc.expectedError != "" { 488 t.Fatalf("validate didn't fail, wanted: %q", err) 489 } 490 }) 491 } 492 } 493 494 func TestCrossCertConfigValidate(t *testing.T) { 495 cases := []struct { 496 name string 497 config crossCertConfig 498 expectedError string 499 }{ 500 { 501 name: "no pkcs11.module", 502 config: crossCertConfig{}, 503 expectedError: "pkcs11.module is required", 504 }, 505 { 506 name: "no pkcs11.signing-key-label", 507 config: crossCertConfig{ 508 PKCS11: PKCS11SigningConfig{ 509 Module: "module", 510 }, 511 }, 512 expectedError: "pkcs11.signing-key-label is required", 513 }, 514 { 515 name: "no inputs.public-key-path", 516 config: crossCertConfig{ 517 PKCS11: PKCS11SigningConfig{ 518 Module: "module", 519 SigningLabel: "label", 520 }, 521 }, 522 expectedError: "inputs.public-key-path is required", 523 }, 524 { 525 name: "no inputs.issuer-certificate-path", 526 config: crossCertConfig{ 527 PKCS11: PKCS11SigningConfig{ 528 Module: "module", 529 SigningLabel: "label", 530 }, 531 Inputs: struct { 532 PublicKeyPath string `yaml:"public-key-path"` 533 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 534 CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"` 535 }{ 536 PublicKeyPath: "path", 537 CertificateToCrossSignPath: "path", 538 }, 539 }, 540 expectedError: "inputs.issuer-certificate is required", 541 }, 542 { 543 name: "no inputs.certificate-to-cross-sign-path", 544 config: crossCertConfig{ 545 PKCS11: PKCS11SigningConfig{ 546 Module: "module", 547 SigningLabel: "label", 548 }, 549 Inputs: struct { 550 PublicKeyPath string `yaml:"public-key-path"` 551 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 552 CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"` 553 }{ 554 PublicKeyPath: "path", 555 IssuerCertificatePath: "path", 556 }, 557 }, 558 expectedError: "inputs.certificate-to-cross-sign-path is required", 559 }, 560 { 561 name: "no outputs.certificate-path", 562 config: crossCertConfig{ 563 PKCS11: PKCS11SigningConfig{ 564 Module: "module", 565 SigningLabel: "label", 566 }, 567 Inputs: struct { 568 PublicKeyPath string `yaml:"public-key-path"` 569 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 570 CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"` 571 }{ 572 PublicKeyPath: "path", 573 IssuerCertificatePath: "path", 574 CertificateToCrossSignPath: "path", 575 }, 576 }, 577 expectedError: "outputs.certificate-path is required", 578 }, 579 { 580 name: "bad certificate-profile", 581 config: crossCertConfig{ 582 PKCS11: PKCS11SigningConfig{ 583 Module: "module", 584 SigningLabel: "label", 585 }, 586 Inputs: struct { 587 PublicKeyPath string `yaml:"public-key-path"` 588 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 589 CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"` 590 }{ 591 PublicKeyPath: "path", 592 IssuerCertificatePath: "path", 593 CertificateToCrossSignPath: "path", 594 }, 595 Outputs: struct { 596 CertificatePath string `yaml:"certificate-path"` 597 }{ 598 CertificatePath: "path", 599 }, 600 }, 601 expectedError: "not-before is required", 602 }, 603 { 604 name: "too many policy OIDs", 605 config: crossCertConfig{ 606 PKCS11: PKCS11SigningConfig{ 607 Module: "module", 608 SigningLabel: "label", 609 }, 610 Inputs: struct { 611 PublicKeyPath string `yaml:"public-key-path"` 612 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 613 CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"` 614 }{ 615 PublicKeyPath: "path", 616 IssuerCertificatePath: "path", 617 CertificateToCrossSignPath: "path", 618 }, 619 Outputs: struct { 620 CertificatePath string `yaml:"certificate-path"` 621 }{ 622 CertificatePath: "path", 623 }, 624 CertProfile: certProfile{ 625 NotBefore: "a", 626 NotAfter: "b", 627 SignatureAlgorithm: "c", 628 CommonName: "d", 629 Organization: "e", 630 Country: "f", 631 CRLURL: "h", 632 IssuerURL: "i", 633 Policies: []policyInfoConfig{{OID: "2.23.140.1.2.1"}, {OID: "6.6.6"}}, 634 }, 635 SkipLints: []string{}, 636 }, 637 expectedError: "policy should be exactly BRs domain-validated for subordinate CAs", 638 }, 639 { 640 name: "too few policy OIDs", 641 config: crossCertConfig{ 642 PKCS11: PKCS11SigningConfig{ 643 Module: "module", 644 SigningLabel: "label", 645 }, 646 Inputs: struct { 647 PublicKeyPath string `yaml:"public-key-path"` 648 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 649 CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"` 650 }{ 651 PublicKeyPath: "path", 652 IssuerCertificatePath: "path", 653 CertificateToCrossSignPath: "path", 654 }, 655 Outputs: struct { 656 CertificatePath string `yaml:"certificate-path"` 657 }{ 658 CertificatePath: "path", 659 }, 660 CertProfile: certProfile{ 661 NotBefore: "a", 662 NotAfter: "b", 663 SignatureAlgorithm: "c", 664 CommonName: "d", 665 Organization: "e", 666 Country: "f", 667 CRLURL: "h", 668 IssuerURL: "i", 669 Policies: []policyInfoConfig{}, 670 }, 671 SkipLints: []string{}, 672 }, 673 expectedError: "policy should be exactly BRs domain-validated for subordinate CAs", 674 }, 675 { 676 name: "good config", 677 config: crossCertConfig{ 678 PKCS11: PKCS11SigningConfig{ 679 Module: "module", 680 SigningLabel: "label", 681 }, 682 Inputs: struct { 683 PublicKeyPath string `yaml:"public-key-path"` 684 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 685 CertificateToCrossSignPath string `yaml:"certificate-to-cross-sign-path"` 686 }{ 687 PublicKeyPath: "path", 688 IssuerCertificatePath: "path", 689 CertificateToCrossSignPath: "path", 690 }, 691 Outputs: struct { 692 CertificatePath string `yaml:"certificate-path"` 693 }{ 694 CertificatePath: "path", 695 }, 696 CertProfile: certProfile{ 697 NotBefore: "a", 698 NotAfter: "b", 699 SignatureAlgorithm: "c", 700 CommonName: "d", 701 Organization: "e", 702 Country: "f", 703 CRLURL: "h", 704 IssuerURL: "i", 705 Policies: []policyInfoConfig{{OID: "2.23.140.1.2.1"}}, 706 }, 707 SkipLints: []string{}, 708 }, 709 }, 710 } 711 for _, tc := range cases { 712 t.Run(tc.name, func(t *testing.T) { 713 err := tc.config.validate() 714 if err != nil && err.Error() != tc.expectedError { 715 t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err) 716 } else if err == nil && tc.expectedError != "" { 717 t.Fatalf("validate didn't fail, wanted: %q", err) 718 } 719 }) 720 } 721 } 722 723 func TestCSRConfigValidate(t *testing.T) { 724 cases := []struct { 725 name string 726 config csrConfig 727 expectedError string 728 }{ 729 { 730 name: "no pkcs11.module", 731 config: csrConfig{}, 732 expectedError: "pkcs11.module is required", 733 }, 734 { 735 name: "no pkcs11.signing-key-label", 736 config: csrConfig{ 737 PKCS11: PKCS11SigningConfig{ 738 Module: "module", 739 }, 740 }, 741 expectedError: "pkcs11.signing-key-label is required", 742 }, 743 { 744 name: "no inputs.public-key-path", 745 config: csrConfig{ 746 PKCS11: PKCS11SigningConfig{ 747 Module: "module", 748 SigningLabel: "label", 749 }, 750 }, 751 expectedError: "inputs.public-key-path is required", 752 }, 753 { 754 name: "no outputs.csr-path", 755 config: csrConfig{ 756 PKCS11: PKCS11SigningConfig{ 757 Module: "module", 758 SigningLabel: "label", 759 }, 760 Inputs: struct { 761 PublicKeyPath string `yaml:"public-key-path"` 762 }{ 763 PublicKeyPath: "path", 764 }, 765 }, 766 expectedError: "outputs.csr-path is required", 767 }, 768 { 769 name: "bad certificate-profile", 770 config: csrConfig{ 771 PKCS11: PKCS11SigningConfig{ 772 Module: "module", 773 SigningLabel: "label", 774 }, 775 Inputs: struct { 776 PublicKeyPath string `yaml:"public-key-path"` 777 }{ 778 PublicKeyPath: "path", 779 }, 780 Outputs: struct { 781 CSRPath string `yaml:"csr-path"` 782 }{ 783 CSRPath: "path", 784 }, 785 }, 786 expectedError: "common-name is required", 787 }, 788 { 789 name: "good config", 790 config: csrConfig{ 791 PKCS11: PKCS11SigningConfig{ 792 Module: "module", 793 SigningLabel: "label", 794 }, 795 Inputs: struct { 796 PublicKeyPath string `yaml:"public-key-path"` 797 }{ 798 PublicKeyPath: "path", 799 }, 800 Outputs: struct { 801 CSRPath string `yaml:"csr-path"` 802 }{ 803 CSRPath: "path", 804 }, 805 CertProfile: certProfile{ 806 CommonName: "d", 807 Organization: "e", 808 Country: "f", 809 }, 810 }, 811 }, 812 } 813 for _, tc := range cases { 814 t.Run(tc.name, func(t *testing.T) { 815 err := tc.config.validate() 816 if err != nil && err.Error() != tc.expectedError { 817 t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err) 818 } else if err == nil && tc.expectedError != "" { 819 t.Fatalf("validate didn't fail, wanted: %q", err) 820 } 821 }) 822 } 823 } 824 825 func TestKeyConfigValidate(t *testing.T) { 826 cases := []struct { 827 name string 828 config keyConfig 829 expectedError string 830 }{ 831 { 832 name: "no pkcs11.module", 833 config: keyConfig{}, 834 expectedError: "pkcs11.module is required", 835 }, 836 { 837 name: "no pkcs11.store-key-with-label", 838 config: keyConfig{ 839 PKCS11: PKCS11KeyGenConfig{ 840 Module: "module", 841 }, 842 }, 843 expectedError: "pkcs11.store-key-with-label is required", 844 }, 845 { 846 name: "bad key fields", 847 config: keyConfig{ 848 PKCS11: PKCS11KeyGenConfig{ 849 Module: "module", 850 StoreLabel: "label", 851 }, 852 }, 853 expectedError: "key.type is required", 854 }, 855 { 856 name: "no outputs.public-key-path", 857 config: keyConfig{ 858 PKCS11: PKCS11KeyGenConfig{ 859 Module: "module", 860 StoreLabel: "label", 861 }, 862 Key: keyGenConfig{ 863 Type: "rsa", 864 RSAModLength: 2048, 865 }, 866 }, 867 expectedError: "outputs.public-key-path is required", 868 }, 869 { 870 name: "good config", 871 config: keyConfig{ 872 PKCS11: PKCS11KeyGenConfig{ 873 Module: "module", 874 StoreLabel: "label", 875 }, 876 Key: keyGenConfig{ 877 Type: "rsa", 878 RSAModLength: 2048, 879 }, 880 Outputs: struct { 881 PublicKeyPath string `yaml:"public-key-path"` 882 PKCS11ConfigPath string `yaml:"pkcs11-config-path"` 883 }{ 884 PublicKeyPath: "path", 885 PKCS11ConfigPath: "path.json", 886 }, 887 }, 888 }, 889 } 890 for _, tc := range cases { 891 t.Run(tc.name, func(t *testing.T) { 892 err := tc.config.validate() 893 if err != nil && err.Error() != tc.expectedError { 894 t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err) 895 } else if err == nil && tc.expectedError != "" { 896 t.Fatalf("validate didn't fail, wanted: %q", err) 897 } 898 }) 899 } 900 } 901 902 func TestCRLConfig(t *testing.T) { 903 cases := []struct { 904 name string 905 config crlConfig 906 expectedError string 907 }{ 908 { 909 name: "no pkcs11.module", 910 config: crlConfig{}, 911 expectedError: "pkcs11.module is required", 912 }, 913 { 914 name: "no pkcs11.signing-key-label", 915 config: crlConfig{ 916 PKCS11: PKCS11SigningConfig{ 917 Module: "module", 918 }, 919 }, 920 expectedError: "pkcs11.signing-key-label is required", 921 }, 922 { 923 name: "no inputs.issuer-certificate-path", 924 config: crlConfig{ 925 PKCS11: PKCS11SigningConfig{ 926 Module: "module", 927 SigningLabel: "label", 928 }, 929 }, 930 expectedError: "inputs.issuer-certificate-path is required", 931 }, 932 { 933 name: "no outputs.crl-path", 934 config: crlConfig{ 935 PKCS11: PKCS11SigningConfig{ 936 Module: "module", 937 SigningLabel: "label", 938 }, 939 Inputs: struct { 940 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 941 }{ 942 IssuerCertificatePath: "path", 943 }, 944 }, 945 expectedError: "outputs.crl-path is required", 946 }, 947 { 948 name: "no crl-profile.this-update", 949 config: crlConfig{ 950 PKCS11: PKCS11SigningConfig{ 951 Module: "module", 952 SigningLabel: "label", 953 }, 954 Inputs: struct { 955 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 956 }{ 957 IssuerCertificatePath: "path", 958 }, 959 Outputs: struct { 960 CRLPath string `yaml:"crl-path"` 961 }{ 962 CRLPath: "path", 963 }, 964 }, 965 expectedError: "crl-profile.this-update is required", 966 }, 967 { 968 name: "no crl-profile.next-update", 969 config: crlConfig{ 970 PKCS11: PKCS11SigningConfig{ 971 Module: "module", 972 SigningLabel: "label", 973 }, 974 Inputs: struct { 975 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 976 }{ 977 IssuerCertificatePath: "path", 978 }, 979 Outputs: struct { 980 CRLPath string `yaml:"crl-path"` 981 }{ 982 CRLPath: "path", 983 }, 984 CRLProfile: struct { 985 ThisUpdate string `yaml:"this-update"` 986 NextUpdate string `yaml:"next-update"` 987 Number int64 `yaml:"number"` 988 RevokedCertificates []struct { 989 CertificatePath string `yaml:"certificate-path"` 990 RevocationDate string `yaml:"revocation-date"` 991 RevocationReason string `yaml:"revocation-reason"` 992 } `yaml:"revoked-certificates"` 993 }{ 994 ThisUpdate: "this-update", 995 }, 996 }, 997 expectedError: "crl-profile.next-update is required", 998 }, 999 { 1000 name: "no crl-profile.number", 1001 config: crlConfig{ 1002 PKCS11: PKCS11SigningConfig{ 1003 Module: "module", 1004 SigningLabel: "label", 1005 }, 1006 Inputs: struct { 1007 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 1008 }{ 1009 IssuerCertificatePath: "path", 1010 }, 1011 Outputs: struct { 1012 CRLPath string `yaml:"crl-path"` 1013 }{ 1014 CRLPath: "path", 1015 }, 1016 CRLProfile: struct { 1017 ThisUpdate string `yaml:"this-update"` 1018 NextUpdate string `yaml:"next-update"` 1019 Number int64 `yaml:"number"` 1020 RevokedCertificates []struct { 1021 CertificatePath string `yaml:"certificate-path"` 1022 RevocationDate string `yaml:"revocation-date"` 1023 RevocationReason string `yaml:"revocation-reason"` 1024 } `yaml:"revoked-certificates"` 1025 }{ 1026 ThisUpdate: "this-update", 1027 NextUpdate: "next-update", 1028 }, 1029 }, 1030 expectedError: "crl-profile.number must be non-zero", 1031 }, 1032 { 1033 name: "no crl-profile.revoked-certificates.certificate-path", 1034 config: crlConfig{ 1035 PKCS11: PKCS11SigningConfig{ 1036 Module: "module", 1037 SigningLabel: "label", 1038 }, 1039 Inputs: struct { 1040 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 1041 }{ 1042 IssuerCertificatePath: "path", 1043 }, 1044 Outputs: struct { 1045 CRLPath string `yaml:"crl-path"` 1046 }{ 1047 CRLPath: "path", 1048 }, 1049 CRLProfile: struct { 1050 ThisUpdate string `yaml:"this-update"` 1051 NextUpdate string `yaml:"next-update"` 1052 Number int64 `yaml:"number"` 1053 RevokedCertificates []struct { 1054 CertificatePath string `yaml:"certificate-path"` 1055 RevocationDate string `yaml:"revocation-date"` 1056 RevocationReason string `yaml:"revocation-reason"` 1057 } `yaml:"revoked-certificates"` 1058 }{ 1059 ThisUpdate: "this-update", 1060 NextUpdate: "next-update", 1061 Number: 1, 1062 RevokedCertificates: []struct { 1063 CertificatePath string `yaml:"certificate-path"` 1064 RevocationDate string `yaml:"revocation-date"` 1065 RevocationReason string `yaml:"revocation-reason"` 1066 }{{}}, 1067 }, 1068 }, 1069 expectedError: "crl-profile.revoked-certificates.certificate-path is required", 1070 }, 1071 { 1072 name: "no crl-profile.revoked-certificates.revocation-date", 1073 config: crlConfig{ 1074 PKCS11: PKCS11SigningConfig{ 1075 Module: "module", 1076 SigningLabel: "label", 1077 }, 1078 Inputs: struct { 1079 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 1080 }{ 1081 IssuerCertificatePath: "path", 1082 }, 1083 Outputs: struct { 1084 CRLPath string `yaml:"crl-path"` 1085 }{ 1086 CRLPath: "path", 1087 }, 1088 CRLProfile: struct { 1089 ThisUpdate string `yaml:"this-update"` 1090 NextUpdate string `yaml:"next-update"` 1091 Number int64 `yaml:"number"` 1092 RevokedCertificates []struct { 1093 CertificatePath string `yaml:"certificate-path"` 1094 RevocationDate string `yaml:"revocation-date"` 1095 RevocationReason string `yaml:"revocation-reason"` 1096 } `yaml:"revoked-certificates"` 1097 }{ 1098 ThisUpdate: "this-update", 1099 NextUpdate: "next-update", 1100 Number: 1, 1101 RevokedCertificates: []struct { 1102 CertificatePath string `yaml:"certificate-path"` 1103 RevocationDate string `yaml:"revocation-date"` 1104 RevocationReason string `yaml:"revocation-reason"` 1105 }{{ 1106 CertificatePath: "path", 1107 }}, 1108 }, 1109 }, 1110 expectedError: "crl-profile.revoked-certificates.revocation-date is required", 1111 }, 1112 { 1113 name: "no revocation reason", 1114 config: crlConfig{ 1115 PKCS11: PKCS11SigningConfig{ 1116 Module: "module", 1117 SigningLabel: "label", 1118 }, 1119 Inputs: struct { 1120 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 1121 }{ 1122 IssuerCertificatePath: "path", 1123 }, 1124 Outputs: struct { 1125 CRLPath string `yaml:"crl-path"` 1126 }{ 1127 CRLPath: "path", 1128 }, 1129 CRLProfile: struct { 1130 ThisUpdate string `yaml:"this-update"` 1131 NextUpdate string `yaml:"next-update"` 1132 Number int64 `yaml:"number"` 1133 RevokedCertificates []struct { 1134 CertificatePath string `yaml:"certificate-path"` 1135 RevocationDate string `yaml:"revocation-date"` 1136 RevocationReason string `yaml:"revocation-reason"` 1137 } `yaml:"revoked-certificates"` 1138 }{ 1139 ThisUpdate: "this-update", 1140 NextUpdate: "next-update", 1141 Number: 1, 1142 RevokedCertificates: []struct { 1143 CertificatePath string `yaml:"certificate-path"` 1144 RevocationDate string `yaml:"revocation-date"` 1145 RevocationReason string `yaml:"revocation-reason"` 1146 }{{ 1147 CertificatePath: "path", 1148 RevocationDate: "date", 1149 }}, 1150 }, 1151 }, 1152 expectedError: "crl-profile.revoked-certificates.revocation-reason is required", 1153 }, 1154 { 1155 name: "good", 1156 config: crlConfig{ 1157 PKCS11: PKCS11SigningConfig{ 1158 Module: "module", 1159 SigningLabel: "label", 1160 }, 1161 Inputs: struct { 1162 IssuerCertificatePath string `yaml:"issuer-certificate-path"` 1163 }{ 1164 IssuerCertificatePath: "path", 1165 }, 1166 Outputs: struct { 1167 CRLPath string `yaml:"crl-path"` 1168 }{ 1169 CRLPath: "path", 1170 }, 1171 CRLProfile: struct { 1172 ThisUpdate string `yaml:"this-update"` 1173 NextUpdate string `yaml:"next-update"` 1174 Number int64 `yaml:"number"` 1175 RevokedCertificates []struct { 1176 CertificatePath string `yaml:"certificate-path"` 1177 RevocationDate string `yaml:"revocation-date"` 1178 RevocationReason string `yaml:"revocation-reason"` 1179 } `yaml:"revoked-certificates"` 1180 }{ 1181 ThisUpdate: "this-update", 1182 NextUpdate: "next-update", 1183 Number: 1, 1184 RevokedCertificates: []struct { 1185 CertificatePath string `yaml:"certificate-path"` 1186 RevocationDate string `yaml:"revocation-date"` 1187 RevocationReason string `yaml:"revocation-reason"` 1188 }{{ 1189 CertificatePath: "path", 1190 RevocationDate: "date", 1191 RevocationReason: "keyCompromise", 1192 }}, 1193 }, 1194 }, 1195 }, 1196 } 1197 for _, tc := range cases { 1198 t.Run(tc.name, func(t *testing.T) { 1199 err := tc.config.validate() 1200 if err != nil && err.Error() != tc.expectedError { 1201 t.Fatalf("Unexpected error, wanted: %q, got: %q", tc.expectedError, err) 1202 } else if err == nil && tc.expectedError != "" { 1203 t.Fatalf("validate didn't fail, wanted: %q", err) 1204 } 1205 }) 1206 } 1207 } 1208 1209 func TestSignAndWriteNoLintCert(t *testing.T) { 1210 _, err := signAndWriteCert(nil, nil, nil, nil, nil, "") 1211 test.AssertError(t, err, "should have failed because no lintCert was provided") 1212 test.AssertDeepEquals(t, err, fmt.Errorf("linting was not performed prior to issuance")) 1213 } 1214 1215 func TestPostIssuanceLinting(t *testing.T) { 1216 clk := clock.New() 1217 err := postIssuanceLinting(nil, nil) 1218 test.AssertError(t, err, "should have failed because no certificate was provided") 1219 1220 testKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) 1221 test.AssertNotError(t, err, "unable to generate ECDSA private key") 1222 template := &x509.Certificate{ 1223 NotAfter: clk.Now().Add(1 * time.Hour), 1224 DNSNames: []string{"example.com"}, 1225 SerialNumber: big.NewInt(1), 1226 } 1227 certDer, err := x509.CreateCertificate(rand.Reader, template, template, &testKey.PublicKey, testKey) 1228 test.AssertNotError(t, err, "unable to create certificate") 1229 parsedCert, err := x509.ParseCertificate(certDer) 1230 test.AssertNotError(t, err, "unable to parse DER bytes") 1231 err = postIssuanceLinting(parsedCert, nil) 1232 test.AssertNotError(t, err, "should not have errored") 1233 }