973 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			973 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Go
		
	
	
	
package issuance
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto"
 | 
						|
	"crypto/dsa"
 | 
						|
	"crypto/ecdsa"
 | 
						|
	"crypto/elliptic"
 | 
						|
	"crypto/rand"
 | 
						|
	"crypto/rsa"
 | 
						|
	"crypto/x509"
 | 
						|
	"crypto/x509/pkix"
 | 
						|
	"encoding/base64"
 | 
						|
	"net"
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
	"time"
 | 
						|
 | 
						|
	ct "github.com/google/certificate-transparency-go"
 | 
						|
	"github.com/jmhodges/clock"
 | 
						|
 | 
						|
	"github.com/letsencrypt/boulder/config"
 | 
						|
	"github.com/letsencrypt/boulder/ctpolicy/loglist"
 | 
						|
	"github.com/letsencrypt/boulder/linter"
 | 
						|
	"github.com/letsencrypt/boulder/test"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	goodSKID = []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
 | 
						|
)
 | 
						|
 | 
						|
func defaultProfile() *Profile {
 | 
						|
	p, _ := NewProfile(defaultProfileConfig())
 | 
						|
	return p
 | 
						|
}
 | 
						|
 | 
						|
func TestGenerateValidity(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Set(time.Date(2015, time.June, 04, 11, 04, 38, 0, time.UTC))
 | 
						|
 | 
						|
	tests := []struct {
 | 
						|
		name      string
 | 
						|
		backdate  time.Duration
 | 
						|
		validity  time.Duration
 | 
						|
		notBefore time.Time
 | 
						|
		notAfter  time.Time
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:      "normal usage",
 | 
						|
			backdate:  time.Hour, // 90% of one hour is 54 minutes
 | 
						|
			validity:  7 * 24 * time.Hour,
 | 
						|
			notBefore: time.Date(2015, time.June, 04, 10, 10, 38, 0, time.UTC),
 | 
						|
			notAfter:  time.Date(2015, time.June, 11, 10, 10, 37, 0, time.UTC),
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:      "zero backdate",
 | 
						|
			backdate:  0,
 | 
						|
			validity:  7 * 24 * time.Hour,
 | 
						|
			notBefore: time.Date(2015, time.June, 04, 11, 04, 38, 0, time.UTC),
 | 
						|
			notAfter:  time.Date(2015, time.June, 11, 11, 04, 37, 0, time.UTC),
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, tc := range tests {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			p := Profile{maxBackdate: tc.backdate, maxValidity: tc.validity}
 | 
						|
			notBefore, notAfter := p.GenerateValidity(fc.Now())
 | 
						|
			test.AssertEquals(t, notBefore, tc.notBefore)
 | 
						|
			test.AssertEquals(t, notAfter, tc.notAfter)
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestCRLURL(t *testing.T) {
 | 
						|
	issuer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, clock.NewFake())
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("newIssuer: %s", err)
 | 
						|
	}
 | 
						|
	url := issuer.crlURL(4928)
 | 
						|
	want := "http://crl-url.example.org/4928.crl"
 | 
						|
	if url != want {
 | 
						|
		t.Errorf("crlURL(4928)=%s, want %s", url, want)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestRequestValid(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Add(time.Hour * 24)
 | 
						|
 | 
						|
	tests := []struct {
 | 
						|
		name          string
 | 
						|
		issuer        *Issuer
 | 
						|
		profile       *Profile
 | 
						|
		request       *IssuanceRequest
 | 
						|
		expectedError string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name:          "unsupported key type",
 | 
						|
			issuer:        &Issuer{},
 | 
						|
			profile:       &Profile{},
 | 
						|
			request:       &IssuanceRequest{PublicKey: MarshalablePublicKey{&dsa.PublicKey{}}},
 | 
						|
			expectedError: "unsupported public key type",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:          "inactive (rsa)",
 | 
						|
			issuer:        &Issuer{},
 | 
						|
			profile:       &Profile{},
 | 
						|
			request:       &IssuanceRequest{PublicKey: MarshalablePublicKey{&rsa.PublicKey{}}},
 | 
						|
			expectedError: "inactive issuer cannot issue precert",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name:          "inactive (ecdsa)",
 | 
						|
			issuer:        &Issuer{},
 | 
						|
			profile:       &Profile{},
 | 
						|
			request:       &IssuanceRequest{PublicKey: MarshalablePublicKey{&ecdsa.PublicKey{}}},
 | 
						|
			expectedError: "inactive issuer cannot issue precert",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "skid too short",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:    MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId: []byte{0, 1, 2, 3, 4},
 | 
						|
			},
 | 
						|
			expectedError: "unexpected subject key ID length",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "both sct list and ct poison provided",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:       MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId:    goodSKID,
 | 
						|
				IncludeCTPoison: true,
 | 
						|
				sctList:         []ct.SignedCertificateTimestamp{},
 | 
						|
			},
 | 
						|
			expectedError: "cannot include both ct poison and sct list extensions",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "negative validity",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:    MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId: goodSKID,
 | 
						|
				NotBefore:    fc.Now().Add(time.Hour),
 | 
						|
				NotAfter:     fc.Now(),
 | 
						|
			},
 | 
						|
			expectedError: "NotAfter must be after NotBefore",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "validity larger than max",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{
 | 
						|
				maxValidity: time.Minute,
 | 
						|
			},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:    MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId: goodSKID,
 | 
						|
				NotBefore:    fc.Now(),
 | 
						|
				NotAfter:     fc.Now().Add(time.Hour - time.Second),
 | 
						|
			},
 | 
						|
			expectedError: "validity period is more than the maximum allowed period (1h0m0s>1m0s)",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "validity larger than max due to inclusivity",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{
 | 
						|
				maxValidity: time.Hour,
 | 
						|
			},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:    MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId: goodSKID,
 | 
						|
				NotBefore:    fc.Now(),
 | 
						|
				NotAfter:     fc.Now().Add(time.Hour),
 | 
						|
			},
 | 
						|
			expectedError: "validity period is more than the maximum allowed period (1h0m1s>1h0m0s)",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "validity backdated more than max",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{
 | 
						|
				maxValidity: time.Hour * 2,
 | 
						|
				maxBackdate: time.Hour,
 | 
						|
			},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:    MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId: goodSKID,
 | 
						|
				NotBefore:    fc.Now().Add(-time.Hour * 2),
 | 
						|
				NotAfter:     fc.Now().Add(-time.Hour),
 | 
						|
			},
 | 
						|
			expectedError: "NotBefore is backdated more than the maximum allowed period (2h0m0s>1h0m0s)",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "validity is forward dated",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{
 | 
						|
				maxValidity: time.Hour * 2,
 | 
						|
				maxBackdate: time.Hour,
 | 
						|
			},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:    MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId: goodSKID,
 | 
						|
				NotBefore:    fc.Now().Add(time.Hour),
 | 
						|
				NotAfter:     fc.Now().Add(time.Hour * 2),
 | 
						|
			},
 | 
						|
			expectedError: "NotBefore is in the future",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "serial too short",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{
 | 
						|
				maxValidity: time.Hour * 2,
 | 
						|
			},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:    MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId: goodSKID,
 | 
						|
				NotBefore:    fc.Now(),
 | 
						|
				NotAfter:     fc.Now().Add(time.Hour),
 | 
						|
				Serial:       []byte{0, 1, 2, 3, 4, 5, 6, 7},
 | 
						|
			},
 | 
						|
			expectedError: "serial must be between 9 and 19 bytes",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "serial too long",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{
 | 
						|
				maxValidity: time.Hour * 2,
 | 
						|
			},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:    MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId: goodSKID,
 | 
						|
				NotBefore:    fc.Now(),
 | 
						|
				NotAfter:     fc.Now().Add(time.Hour),
 | 
						|
				Serial:       []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
			},
 | 
						|
			expectedError: "serial must be between 9 and 19 bytes",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "good with poison",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{
 | 
						|
				maxValidity: time.Hour * 2,
 | 
						|
			},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:       MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId:    goodSKID,
 | 
						|
				NotBefore:       fc.Now(),
 | 
						|
				NotAfter:        fc.Now().Add(time.Hour),
 | 
						|
				Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
				IncludeCTPoison: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "good with scts",
 | 
						|
			issuer: &Issuer{
 | 
						|
				active: true,
 | 
						|
			},
 | 
						|
			profile: &Profile{
 | 
						|
				maxValidity: time.Hour * 2,
 | 
						|
			},
 | 
						|
			request: &IssuanceRequest{
 | 
						|
				PublicKey:    MarshalablePublicKey{&ecdsa.PublicKey{}},
 | 
						|
				SubjectKeyId: goodSKID,
 | 
						|
				NotBefore:    fc.Now(),
 | 
						|
				NotAfter:     fc.Now().Add(time.Hour),
 | 
						|
				Serial:       []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
				sctList:      []ct.SignedCertificateTimestamp{},
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tc := range tests {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			err := tc.issuer.requestValid(fc, tc.profile, tc.request)
 | 
						|
			if err != nil {
 | 
						|
				if tc.expectedError == "" {
 | 
						|
					t.Errorf("failed with unexpected error: %s", err)
 | 
						|
				} else if tc.expectedError != err.Error() {
 | 
						|
					t.Errorf("failed with unexpected error, wanted: %q, got: %q", tc.expectedError, err.Error())
 | 
						|
				}
 | 
						|
				return
 | 
						|
			} else if tc.expectedError != "" {
 | 
						|
				t.Errorf("didn't fail, expected %q", tc.expectedError)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestGenerateTemplate(t *testing.T) {
 | 
						|
	issuer := &Issuer{
 | 
						|
		issuerURL:  "http://issuer",
 | 
						|
		crlURLBase: "http://crl/",
 | 
						|
		sigAlg:     x509.SHA256WithRSA,
 | 
						|
	}
 | 
						|
 | 
						|
	actual := issuer.generateTemplate()
 | 
						|
 | 
						|
	expected := &x509.Certificate{
 | 
						|
		BasicConstraintsValid: true,
 | 
						|
		SignatureAlgorithm:    x509.SHA256WithRSA,
 | 
						|
		IssuingCertificateURL: []string{"http://issuer"},
 | 
						|
		Policies:              []x509.OID{domainValidatedOID},
 | 
						|
		// These fields are only included if specified in the profile.
 | 
						|
		OCSPServer:            nil,
 | 
						|
		CRLDistributionPoints: nil,
 | 
						|
	}
 | 
						|
 | 
						|
	test.AssertDeepEquals(t, actual, expected)
 | 
						|
}
 | 
						|
 | 
						|
func TestIssue(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		name         string
 | 
						|
		generateFunc func() (crypto.Signer, error)
 | 
						|
		ku           x509.KeyUsage
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name: "RSA",
 | 
						|
			generateFunc: func() (crypto.Signer, error) {
 | 
						|
				return rsa.GenerateKey(rand.Reader, 2048)
 | 
						|
			},
 | 
						|
			ku: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "ECDSA",
 | 
						|
			generateFunc: func() (crypto.Signer, error) {
 | 
						|
				return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
			},
 | 
						|
			ku: x509.KeyUsageDigitalSignature,
 | 
						|
		},
 | 
						|
	} {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			fc := clock.NewFake()
 | 
						|
			fc.Set(time.Now())
 | 
						|
			signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
			test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
			pk, err := tc.generateFunc()
 | 
						|
			test.AssertNotError(t, err, "failed to generate test key")
 | 
						|
			lintCertBytes, issuanceToken, err := signer.Prepare(defaultProfile(), &IssuanceRequest{
 | 
						|
				PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
				SubjectKeyId:    goodSKID,
 | 
						|
				Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
				DNSNames:        []string{"example.com"},
 | 
						|
				IPAddresses:     []net.IP{net.ParseIP("128.101.101.101"), net.ParseIP("3fff:aaa:a:c0ff:ee:a:bad:deed")},
 | 
						|
				NotBefore:       fc.Now(),
 | 
						|
				NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
				IncludeCTPoison: true,
 | 
						|
			})
 | 
						|
			test.AssertNotError(t, err, "Prepare failed")
 | 
						|
			_, err = x509.ParseCertificate(lintCertBytes)
 | 
						|
			test.AssertNotError(t, err, "failed to parse certificate")
 | 
						|
			certBytes, err := signer.Issue(issuanceToken)
 | 
						|
			test.AssertNotError(t, err, "Issue failed")
 | 
						|
			cert, err := x509.ParseCertificate(certBytes)
 | 
						|
			test.AssertNotError(t, err, "failed to parse certificate")
 | 
						|
			err = cert.CheckSignatureFrom(issuerCert.Certificate)
 | 
						|
			test.AssertNotError(t, err, "signature validation failed")
 | 
						|
			test.AssertDeepEquals(t, cert.DNSNames, []string{"example.com"})
 | 
						|
			// net.ParseIP always returns a 16-byte address; IPv4 addresses are
 | 
						|
			// returned in IPv4-mapped IPv6 form. But RFC 5280, Sec. 4.2.1.6
 | 
						|
			// requires that IPv4 addresses be encoded as 4 bytes.
 | 
						|
			//
 | 
						|
			// The issuance pipeline calls x509.marshalSANs, which reduces IPv4
 | 
						|
			// addresses back to 4 bytes. Adding .To4() both allows this test to
 | 
						|
			// succeed, and covers this requirement.
 | 
						|
			test.AssertDeepEquals(t, cert.IPAddresses, []net.IP{net.ParseIP("128.101.101.101").To4(), net.ParseIP("3fff:aaa:a:c0ff:ee:a:bad:deed")})
 | 
						|
			test.AssertByteEquals(t, cert.SerialNumber.Bytes(), []byte{1, 2, 3, 4, 5, 6, 7, 8, 9})
 | 
						|
			test.AssertDeepEquals(t, cert.PublicKey, pk.Public())
 | 
						|
			test.AssertEquals(t, len(cert.Extensions), 10) // Constraints, KU, EKU, SKID, AKID, AIA, CRLDP, SAN, Policies, Poison
 | 
						|
			test.AssertEquals(t, cert.KeyUsage, tc.ku)
 | 
						|
			if len(cert.CRLDistributionPoints) != 1 || !strings.HasPrefix(cert.CRLDistributionPoints[0], "http://crl-url.example.org/") {
 | 
						|
				t.Errorf("want CRLDistributionPoints=[http://crl-url.example.org/x.crl], got %v", cert.CRLDistributionPoints)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestIssueDNSNamesOnly(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("newIssuer: %s", err)
 | 
						|
	}
 | 
						|
	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("ecdsa.GenerateKey: %s", err)
 | 
						|
	}
 | 
						|
	_, issuanceToken, err := signer.Prepare(defaultProfile(), &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example.com"},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("signer.Prepare: %s", err)
 | 
						|
	}
 | 
						|
	certBytes, err := signer.Issue(issuanceToken)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("signer.Issue: %s", err)
 | 
						|
	}
 | 
						|
	cert, err := x509.ParseCertificate(certBytes)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("x509.ParseCertificate: %s", err)
 | 
						|
	}
 | 
						|
	if !reflect.DeepEqual(cert.DNSNames, []string{"example.com"}) {
 | 
						|
		t.Errorf("got DNSNames %s, wanted example.com", cert.DNSNames)
 | 
						|
	}
 | 
						|
	// BRs 7.1.2.7.12 requires iPAddress, if present, to contain an entry.
 | 
						|
	if cert.IPAddresses != nil {
 | 
						|
		t.Errorf("got IPAddresses %s, wanted nil", cert.IPAddresses)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestIssueIPAddressesOnly(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("newIssuer: %s", err)
 | 
						|
	}
 | 
						|
	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("ecdsa.GenerateKey: %s", err)
 | 
						|
	}
 | 
						|
	_, issuanceToken, err := signer.Prepare(defaultProfile(), &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		IPAddresses:     []net.IP{net.ParseIP("128.101.101.101"), net.ParseIP("3fff:aaa:a:c0ff:ee:a:bad:deed")},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("signer.Prepare: %s", err)
 | 
						|
	}
 | 
						|
	certBytes, err := signer.Issue(issuanceToken)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("signer.Issue: %s", err)
 | 
						|
	}
 | 
						|
	cert, err := x509.ParseCertificate(certBytes)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("x509.ParseCertificate: %s", err)
 | 
						|
	}
 | 
						|
	// BRs 7.1.2.7.12 requires dNSName, if present, to contain an entry.
 | 
						|
	if cert.DNSNames != nil {
 | 
						|
		t.Errorf("got DNSNames %s, wanted nil", cert.DNSNames)
 | 
						|
	}
 | 
						|
	if !reflect.DeepEqual(cert.IPAddresses, []net.IP{net.ParseIP("128.101.101.101").To4(), net.ParseIP("3fff:aaa:a:c0ff:ee:a:bad:deed")}) {
 | 
						|
		t.Errorf("got IPAddresses %s, wanted 128.101.101.101 (4-byte) & 3fff:aaa:a:c0ff:ee:a:bad:deed (16-byte)", cert.IPAddresses)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestIssueWithCRLDP(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	issuerConfig := defaultIssuerConfig()
 | 
						|
	issuerConfig.CRLURLBase = "http://crls.example.net/"
 | 
						|
	issuerConfig.CRLShards = 999
 | 
						|
	signer, err := newIssuer(issuerConfig, issuerCert, issuerSigner, fc)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("newIssuer: %s", err)
 | 
						|
	}
 | 
						|
	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("ecdsa.GenerateKey: %s", err)
 | 
						|
	}
 | 
						|
	profile := defaultProfile()
 | 
						|
	profile.includeCRLDistributionPoints = true
 | 
						|
	_, issuanceToken, err := signer.Prepare(profile, &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example.com"},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("signer.Prepare: %s", err)
 | 
						|
	}
 | 
						|
	certBytes, err := signer.Issue(issuanceToken)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("signer.Issue: %s", err)
 | 
						|
	}
 | 
						|
	cert, err := x509.ParseCertificate(certBytes)
 | 
						|
	if err != nil {
 | 
						|
		t.Fatalf("x509.ParseCertificate: %s", err)
 | 
						|
	}
 | 
						|
	// Because CRL shard is calculated deterministically from serial, we know which shard will be chosen.
 | 
						|
	expectedCRLDP := []string{"http://crls.example.net/919.crl"}
 | 
						|
	if !reflect.DeepEqual(cert.CRLDistributionPoints, expectedCRLDP) {
 | 
						|
		t.Errorf("CRLDP=%+v, want %+v", cert.CRLDistributionPoints, expectedCRLDP)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestIssueCommonName(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Set(time.Now())
 | 
						|
 | 
						|
	prof := defaultProfileConfig()
 | 
						|
	prof.IgnoredLints = append(prof.IgnoredLints, "w_subject_common_name_included")
 | 
						|
	cnProfile, err := NewProfile(prof)
 | 
						|
	test.AssertNotError(t, err, "NewProfile failed")
 | 
						|
	signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	test.AssertNotError(t, err, "failed to generate test key")
 | 
						|
	ir := &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example.com", "www.example.com"},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
	}
 | 
						|
 | 
						|
	// In the default profile, the common name is allowed if requested.
 | 
						|
	ir.CommonName = "example.com"
 | 
						|
	_, issuanceToken, err := signer.Prepare(cnProfile, ir)
 | 
						|
	test.AssertNotError(t, err, "Prepare failed")
 | 
						|
	certBytes, err := signer.Issue(issuanceToken)
 | 
						|
	test.AssertNotError(t, err, "Issue failed")
 | 
						|
	cert, err := x509.ParseCertificate(certBytes)
 | 
						|
	test.AssertNotError(t, err, "failed to parse certificate")
 | 
						|
	test.AssertEquals(t, cert.Subject.CommonName, "example.com")
 | 
						|
 | 
						|
	// But not including the common name should be acceptable as well.
 | 
						|
	ir.CommonName = ""
 | 
						|
	_, issuanceToken, err = signer.Prepare(cnProfile, ir)
 | 
						|
	test.AssertNotError(t, err, "Prepare failed")
 | 
						|
	certBytes, err = signer.Issue(issuanceToken)
 | 
						|
	test.AssertNotError(t, err, "Issue failed")
 | 
						|
	cert, err = x509.ParseCertificate(certBytes)
 | 
						|
	test.AssertNotError(t, err, "failed to parse certificate")
 | 
						|
	test.AssertEquals(t, cert.Subject.CommonName, "")
 | 
						|
 | 
						|
	// And the common name should be omitted if the profile is so configured.
 | 
						|
	ir.CommonName = "example.com"
 | 
						|
	cnProfile.omitCommonName = true
 | 
						|
	_, issuanceToken, err = signer.Prepare(cnProfile, ir)
 | 
						|
	test.AssertNotError(t, err, "Prepare failed")
 | 
						|
	certBytes, err = signer.Issue(issuanceToken)
 | 
						|
	test.AssertNotError(t, err, "Issue failed")
 | 
						|
	cert, err = x509.ParseCertificate(certBytes)
 | 
						|
	test.AssertNotError(t, err, "failed to parse certificate")
 | 
						|
	test.AssertEquals(t, cert.Subject.CommonName, "")
 | 
						|
}
 | 
						|
 | 
						|
func TestIssueOmissions(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Set(time.Now())
 | 
						|
 | 
						|
	pc := defaultProfileConfig()
 | 
						|
	pc.OmitCommonName = true
 | 
						|
	pc.OmitKeyEncipherment = true
 | 
						|
	pc.OmitClientAuth = true
 | 
						|
	pc.OmitSKID = true
 | 
						|
	pc.IgnoredLints = []string{
 | 
						|
		// Reduce the lint ignores to just the minimal (SCT-related) set.
 | 
						|
		"w_ct_sct_policy_count_unsatisfied",
 | 
						|
		"e_scts_from_same_operator",
 | 
						|
		// Ignore the warning about *not* including the SubjectKeyIdentifier extension:
 | 
						|
		// zlint has both lints (one enforcing RFC5280, the other the BRs).
 | 
						|
		"w_ext_subject_key_identifier_missing_sub_cert",
 | 
						|
	}
 | 
						|
	prof, err := NewProfile(pc)
 | 
						|
	test.AssertNotError(t, err, "building test profile")
 | 
						|
 | 
						|
	signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
 | 
						|
	pk, err := rsa.GenerateKey(rand.Reader, 2048)
 | 
						|
	test.AssertNotError(t, err, "failed to generate test key")
 | 
						|
	_, issuanceToken, err := signer.Prepare(prof, &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example.com"},
 | 
						|
		CommonName:      "example.com",
 | 
						|
		IncludeCTPoison: true,
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
	})
 | 
						|
	test.AssertNotError(t, err, "Prepare failed")
 | 
						|
	certBytes, err := signer.Issue(issuanceToken)
 | 
						|
	test.AssertNotError(t, err, "Issue failed")
 | 
						|
	cert, err := x509.ParseCertificate(certBytes)
 | 
						|
	test.AssertNotError(t, err, "failed to parse certificate")
 | 
						|
 | 
						|
	test.AssertEquals(t, cert.Subject.CommonName, "")
 | 
						|
	test.AssertEquals(t, cert.KeyUsage, x509.KeyUsageDigitalSignature)
 | 
						|
	test.AssertDeepEquals(t, cert.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth})
 | 
						|
	test.AssertEquals(t, len(cert.SubjectKeyId), 0)
 | 
						|
}
 | 
						|
 | 
						|
func TestIssueCTPoison(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Set(time.Now())
 | 
						|
	signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	test.AssertNotError(t, err, "failed to generate test key")
 | 
						|
	_, issuanceToken, err := signer.Prepare(defaultProfile(), &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example.com"},
 | 
						|
		IncludeCTPoison: true,
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
	})
 | 
						|
	test.AssertNotError(t, err, "Prepare failed")
 | 
						|
	certBytes, err := signer.Issue(issuanceToken)
 | 
						|
	test.AssertNotError(t, err, "Issue failed")
 | 
						|
	cert, err := x509.ParseCertificate(certBytes)
 | 
						|
	test.AssertNotError(t, err, "failed to parse certificate")
 | 
						|
	err = cert.CheckSignatureFrom(issuerCert.Certificate)
 | 
						|
	test.AssertNotError(t, err, "signature validation failed")
 | 
						|
	test.AssertByteEquals(t, cert.SerialNumber.Bytes(), []byte{1, 2, 3, 4, 5, 6, 7, 8, 9})
 | 
						|
	test.AssertDeepEquals(t, cert.PublicKey, pk.Public())
 | 
						|
	test.AssertEquals(t, len(cert.Extensions), 10) // Constraints, KU, EKU, SKID, AKID, AIA, CRLDP, SAN, Policies, Poison
 | 
						|
	test.AssertDeepEquals(t, cert.Extensions[9], ctPoisonExt)
 | 
						|
}
 | 
						|
 | 
						|
func mustDecodeB64(b string) []byte {
 | 
						|
	out, err := base64.StdEncoding.DecodeString(b)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
func TestIssueSCTList(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Set(time.Now())
 | 
						|
 | 
						|
	err := loglist.InitLintList("../test/ct-test-srv/log_list.json")
 | 
						|
	test.AssertNotError(t, err, "failed to load log list")
 | 
						|
 | 
						|
	pc := defaultProfileConfig()
 | 
						|
	pc.IgnoredLints = []string{
 | 
						|
		// Only ignore the SKID lint, i.e., don't ignore the "missing SCT" lints.
 | 
						|
		"w_ext_subject_key_identifier_not_recommended_subscriber",
 | 
						|
	}
 | 
						|
	enforceSCTsProfile, err := NewProfile(pc)
 | 
						|
	test.AssertNotError(t, err, "NewProfile failed")
 | 
						|
	signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	test.AssertNotError(t, err, "failed to generate test key")
 | 
						|
	_, issuanceToken, err := signer.Prepare(enforceSCTsProfile, &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example.com"},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
	})
 | 
						|
	test.AssertNotError(t, err, "Prepare failed")
 | 
						|
	precertBytes, err := signer.Issue(issuanceToken)
 | 
						|
	test.AssertNotError(t, err, "Issue failed")
 | 
						|
	precert, err := x509.ParseCertificate(precertBytes)
 | 
						|
	test.AssertNotError(t, err, "failed to parse certificate")
 | 
						|
 | 
						|
	sctList := []ct.SignedCertificateTimestamp{
 | 
						|
		{
 | 
						|
			SCTVersion: ct.V1,
 | 
						|
			LogID:      ct.LogID{KeyID: *(*[32]byte)(mustDecodeB64("OJiMlNA1mMOTLd/pI7q68npCDrlsQeFaqAwasPwEvQM="))},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			SCTVersion: ct.V1,
 | 
						|
			LogID:      ct.LogID{KeyID: *(*[32]byte)(mustDecodeB64("UtToynGEyMkkXDMQei8Ll54oMwWHI0IieDEKs12/Td4="))},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	request2, err := RequestFromPrecert(precert, sctList)
 | 
						|
	test.AssertNotError(t, err, "generating request from precert")
 | 
						|
 | 
						|
	_, issuanceToken2, err := signer.Prepare(enforceSCTsProfile, request2)
 | 
						|
	test.AssertNotError(t, err, "preparing final cert issuance")
 | 
						|
 | 
						|
	finalCertBytes, err := signer.Issue(issuanceToken2)
 | 
						|
	test.AssertNotError(t, err, "Issue failed")
 | 
						|
 | 
						|
	finalCert, err := x509.ParseCertificate(finalCertBytes)
 | 
						|
	test.AssertNotError(t, err, "failed to parse certificate")
 | 
						|
 | 
						|
	err = finalCert.CheckSignatureFrom(issuerCert.Certificate)
 | 
						|
	test.AssertNotError(t, err, "signature validation failed")
 | 
						|
	test.AssertByteEquals(t, finalCert.SerialNumber.Bytes(), []byte{1, 2, 3, 4, 5, 6, 7, 8, 9})
 | 
						|
	test.AssertDeepEquals(t, finalCert.PublicKey, pk.Public())
 | 
						|
	test.AssertEquals(t, len(finalCert.Extensions), 10) // Constraints, KU, EKU, SKID, AKID, AIA, CRLDP, SAN, Policies, Poison
 | 
						|
	test.AssertDeepEquals(t, finalCert.Extensions[9], pkix.Extension{
 | 
						|
		Id: sctListOID,
 | 
						|
		Value: []byte{
 | 
						|
			4, 100, 0, 98, 0, 47, 0, 56, 152, 140, 148, 208, 53, 152, 195, 147, 45,
 | 
						|
			223, 233, 35, 186, 186, 242, 122, 66, 14, 185, 108, 65, 225, 90, 168, 12,
 | 
						|
			26, 176, 252, 4, 189, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 47,
 | 
						|
			0, 82, 212, 232, 202, 113, 132, 200, 201, 36, 92, 51, 16, 122, 47, 11,
 | 
						|
			151, 158, 40, 51, 5, 135, 35, 66, 34, 120, 49, 10, 179, 93, 191, 77, 222,
 | 
						|
			0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 | 
						|
		},
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func TestIssueBadLint(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Set(time.Now())
 | 
						|
 | 
						|
	pc := defaultProfileConfig()
 | 
						|
	pc.IgnoredLints = []string{}
 | 
						|
	noSkipLintsProfile, err := NewProfile(pc)
 | 
						|
	test.AssertNotError(t, err, "NewProfile failed")
 | 
						|
	signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	test.AssertNotError(t, err, "failed to generate test key")
 | 
						|
	_, _, err = signer.Prepare(noSkipLintsProfile, &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example-com"},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
	})
 | 
						|
	test.AssertError(t, err, "Prepare didn't fail")
 | 
						|
	test.AssertErrorIs(t, err, linter.ErrLinting)
 | 
						|
	test.AssertContains(t, err.Error(), "tbsCertificate linting failed: failed lint(s)")
 | 
						|
}
 | 
						|
 | 
						|
func TestIssuanceToken(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Set(time.Now())
 | 
						|
 | 
						|
	signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
 | 
						|
	_, err = signer.Issue(&issuanceToken{})
 | 
						|
	test.AssertError(t, err, "expected issuance with a zero token to fail")
 | 
						|
 | 
						|
	_, err = signer.Issue(nil)
 | 
						|
	test.AssertError(t, err, "expected issuance with a nil token to fail")
 | 
						|
 | 
						|
	pk, err := rsa.GenerateKey(rand.Reader, 2048)
 | 
						|
	test.AssertNotError(t, err, "failed to generate test key")
 | 
						|
	_, issuanceToken, err := signer.Prepare(defaultProfile(), &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example.com"},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
	})
 | 
						|
	test.AssertNotError(t, err, "expected Prepare to succeed")
 | 
						|
	_, err = signer.Issue(issuanceToken)
 | 
						|
	test.AssertNotError(t, err, "expected first issuance to succeed")
 | 
						|
 | 
						|
	_, err = signer.Issue(issuanceToken)
 | 
						|
	test.AssertError(t, err, "expected second issuance with the same issuance token to fail")
 | 
						|
	test.AssertContains(t, err.Error(), "issuance token already redeemed")
 | 
						|
 | 
						|
	_, issuanceToken, err = signer.Prepare(defaultProfile(), &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example.com"},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
	})
 | 
						|
	test.AssertNotError(t, err, "expected Prepare to succeed")
 | 
						|
 | 
						|
	signer2, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
 | 
						|
	_, err = signer2.Issue(issuanceToken)
 | 
						|
	test.AssertError(t, err, "expected redeeming an issuance token with the wrong issuer to fail")
 | 
						|
	test.AssertContains(t, err.Error(), "wrong issuer")
 | 
						|
}
 | 
						|
 | 
						|
func TestInvalidProfile(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Set(time.Now())
 | 
						|
 | 
						|
	err := loglist.InitLintList("../test/ct-test-srv/log_list.json")
 | 
						|
	test.AssertNotError(t, err, "failed to load log list")
 | 
						|
 | 
						|
	signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	test.AssertNotError(t, err, "failed to generate test key")
 | 
						|
	_, _, err = signer.Prepare(defaultProfile(), &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:        []string{"example.com"},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
		precertDER:      []byte{6, 6, 6},
 | 
						|
	})
 | 
						|
	test.AssertError(t, err, "Invalid IssuanceRequest")
 | 
						|
 | 
						|
	_, _, err = signer.Prepare(defaultProfile(), &IssuanceRequest{
 | 
						|
		PublicKey:    MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId: goodSKID,
 | 
						|
		Serial:       []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		DNSNames:     []string{"example.com"},
 | 
						|
		NotBefore:    fc.Now(),
 | 
						|
		NotAfter:     fc.Now().Add(time.Hour - time.Second),
 | 
						|
		sctList: []ct.SignedCertificateTimestamp{
 | 
						|
			{
 | 
						|
				SCTVersion: ct.V1,
 | 
						|
				LogID:      ct.LogID{KeyID: *(*[32]byte)(mustDecodeB64("OJiMlNA1mMOTLd/pI7q68npCDrlsQeFaqAwasPwEvQM="))},
 | 
						|
			},
 | 
						|
		},
 | 
						|
		precertDER: []byte{},
 | 
						|
	})
 | 
						|
	test.AssertError(t, err, "Invalid IssuanceRequest")
 | 
						|
}
 | 
						|
 | 
						|
// Generate a precert from one profile and a final cert from another, and verify
 | 
						|
// that the final cert errors out when linted because the lint cert doesn't
 | 
						|
// corresponding with the precert.
 | 
						|
func TestMismatchedProfiles(t *testing.T) {
 | 
						|
	fc := clock.NewFake()
 | 
						|
	fc.Set(time.Now())
 | 
						|
	err := loglist.InitLintList("../test/ct-test-srv/log_list.json")
 | 
						|
	test.AssertNotError(t, err, "failed to load log list")
 | 
						|
 | 
						|
	issuer1, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
 | 
						|
	pc := defaultProfileConfig()
 | 
						|
	pc.IgnoredLints = append(pc.IgnoredLints, "w_subject_common_name_included")
 | 
						|
	cnProfile, err := NewProfile(pc)
 | 
						|
	test.AssertNotError(t, err, "NewProfile failed")
 | 
						|
 | 
						|
	pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
 | 
						|
	test.AssertNotError(t, err, "failed to generate test key")
 | 
						|
	_, issuanceToken, err := issuer1.Prepare(cnProfile, &IssuanceRequest{
 | 
						|
		PublicKey:       MarshalablePublicKey{pk.Public()},
 | 
						|
		SubjectKeyId:    goodSKID,
 | 
						|
		Serial:          []byte{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
						|
		CommonName:      "example.com",
 | 
						|
		DNSNames:        []string{"example.com"},
 | 
						|
		NotBefore:       fc.Now(),
 | 
						|
		NotAfter:        fc.Now().Add(time.Hour - time.Second),
 | 
						|
		IncludeCTPoison: true,
 | 
						|
	})
 | 
						|
	test.AssertNotError(t, err, "making IssuanceRequest")
 | 
						|
 | 
						|
	precertDER, err := issuer1.Issue(issuanceToken)
 | 
						|
	test.AssertNotError(t, err, "signing precert")
 | 
						|
 | 
						|
	// Create a new profile that differs slightly (no common name)
 | 
						|
	pc = defaultProfileConfig()
 | 
						|
	pc.OmitCommonName = false
 | 
						|
	test.AssertNotError(t, err, "building test lint registry")
 | 
						|
	noCNProfile, err := NewProfile(pc)
 | 
						|
	test.AssertNotError(t, err, "NewProfile failed")
 | 
						|
 | 
						|
	issuer2, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
 | 
						|
	test.AssertNotError(t, err, "NewIssuer failed")
 | 
						|
 | 
						|
	sctList := []ct.SignedCertificateTimestamp{
 | 
						|
		{
 | 
						|
			SCTVersion: ct.V1,
 | 
						|
			LogID:      ct.LogID{KeyID: *(*[32]byte)(mustDecodeB64("OJiMlNA1mMOTLd/pI7q68npCDrlsQeFaqAwasPwEvQM="))},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			SCTVersion: ct.V1,
 | 
						|
			LogID:      ct.LogID{KeyID: *(*[32]byte)(mustDecodeB64("UtToynGEyMkkXDMQei8Ll54oMwWHI0IieDEKs12/Td4="))},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	precert, err := x509.ParseCertificate(precertDER)
 | 
						|
	test.AssertNotError(t, err, "parsing precert")
 | 
						|
 | 
						|
	request2, err := RequestFromPrecert(precert, sctList)
 | 
						|
	test.AssertNotError(t, err, "RequestFromPrecert")
 | 
						|
	request2.CommonName = ""
 | 
						|
 | 
						|
	_, _, err = issuer2.Prepare(noCNProfile, request2)
 | 
						|
	test.AssertError(t, err, "preparing final cert issuance")
 | 
						|
	test.AssertContains(t, err.Error(), "precert does not correspond to linted final cert")
 | 
						|
}
 | 
						|
 | 
						|
func TestNewProfile(t *testing.T) {
 | 
						|
	for _, tc := range []struct {
 | 
						|
		name    string
 | 
						|
		config  ProfileConfig
 | 
						|
		wantErr string
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name: "happy path",
 | 
						|
			config: ProfileConfig{
 | 
						|
				MaxValidityBackdate:          config.Duration{Duration: 1 * time.Hour},
 | 
						|
				MaxValidityPeriod:            config.Duration{Duration: 90 * 24 * time.Hour},
 | 
						|
				IncludeCRLDistributionPoints: true,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "large backdate",
 | 
						|
			config: ProfileConfig{
 | 
						|
				MaxValidityBackdate: config.Duration{Duration: 24 * time.Hour},
 | 
						|
				MaxValidityPeriod:   config.Duration{Duration: 90 * 24 * time.Hour},
 | 
						|
			},
 | 
						|
			wantErr: "backdate \"24h0m0s\" is too large",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "large validity",
 | 
						|
			config: ProfileConfig{
 | 
						|
				MaxValidityBackdate: config.Duration{Duration: 1 * time.Hour},
 | 
						|
				MaxValidityPeriod:   config.Duration{Duration: 397 * 24 * time.Hour},
 | 
						|
			},
 | 
						|
			wantErr: "validity period \"9528h0m0s\" is too large",
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "no revocation info",
 | 
						|
			config: ProfileConfig{
 | 
						|
				MaxValidityBackdate:          config.Duration{Duration: 1 * time.Hour},
 | 
						|
				MaxValidityPeriod:            config.Duration{Duration: 90 * 24 * time.Hour},
 | 
						|
				IncludeCRLDistributionPoints: false,
 | 
						|
			},
 | 
						|
			wantErr: "revocation mechanism must be included",
 | 
						|
		},
 | 
						|
	} {
 | 
						|
		t.Run(tc.name, func(t *testing.T) {
 | 
						|
			gotProfile, gotErr := NewProfile(&tc.config)
 | 
						|
			if tc.wantErr != "" {
 | 
						|
				if gotErr == nil {
 | 
						|
					t.Errorf("NewProfile(%#v) = %#v, but want err %q", tc.config, gotProfile, tc.wantErr)
 | 
						|
				}
 | 
						|
				if !strings.Contains(gotErr.Error(), tc.wantErr) {
 | 
						|
					t.Errorf("NewProfile(%#v) = %q, but want %q", tc.config, gotErr, tc.wantErr)
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				if gotErr != nil {
 | 
						|
					t.Errorf("NewProfile(%#v) = %q, but want no error", tc.config, gotErr)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 |