Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 29 additions & 10 deletions issuance/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type ProfileConfig struct {
OmitClientAuth bool
// OmitSKID causes the Subject Key Identifier extension to be omitted.
OmitSKID bool
// MTC causes the precertificate poison and SCT list extension to be omitted.
MTC bool

MaxValidityPeriod config.Duration
MaxValidityBackdate config.Duration
Expand All @@ -68,6 +70,7 @@ type Profile struct {
omitKeyEncipherment bool
omitClientAuth bool
omitSKID bool
mtc bool

maxBackdate time.Duration
maxValidity time.Duration
Expand All @@ -91,6 +94,12 @@ func NewProfile(profileConfig ProfileConfig) (*Profile, error) {
return nil, fmt.Errorf("validity period %q is too large", profileConfig.MaxValidityPeriod.Duration)
}

// CQRP: clientAuth is MUST NOT.
// https://docs.google.com/document/d/1bC958-AaZ7ePCPFVyP9Sg2VZ3DcC2-PqwsMly8oIJSU/edit?tab=t.0#heading=h.kbidyave6lzr
if profileConfig.MTC {
profileConfig.OmitClientAuth = true
}

lints, err := linter.NewRegistry(profileConfig.IgnoredLints)
cmd.FailOnError(err, "Failed to create zlint registry")
if profileConfig.LintConfig != "" {
Expand All @@ -104,6 +113,7 @@ func NewProfile(profileConfig ProfileConfig) (*Profile, error) {
omitKeyEncipherment: profileConfig.OmitKeyEncipherment,
omitClientAuth: profileConfig.OmitClientAuth,
omitSKID: profileConfig.OmitSKID,
mtc: profileConfig.MTC,
maxBackdate: profileConfig.MaxValidityBackdate.Duration,
maxValidity: profileConfig.MaxValidityPeriod.Duration,
maxCertificateSize: profileConfig.MaxCertificateSize,
Expand Down Expand Up @@ -343,19 +353,28 @@ func (i *Issuer) Prepare(prof *Profile, req *IssuanceRequest) ([]byte, *issuance
template.SubjectKeyId = req.SubjectKeyId
}

if req.IncludeCTPoison {
template.ExtraExtensions = append(template.ExtraExtensions, ctPoisonExt)
} else if len(req.sctList) > 0 {
if len(req.precertDER) == 0 {
return nil, nil, errors.New("inconsistent request contains sctList but no precertDER")
if prof.mtc {
if req.IncludeCTPoison {
return nil, nil, errors.New("invalid request for CT poison with MTC")
}
sctListExt, err := generateSCTListExt(req.sctList)
if err != nil {
return nil, nil, err
if len(req.sctList) > 0 {
return nil, nil, errors.New("invalid request for SCT list with MTC")
}
template.ExtraExtensions = append(template.ExtraExtensions, sctListExt)
} else {
return nil, nil, errors.New("invalid request contains neither sctList nor precertDER")
if req.IncludeCTPoison {
template.ExtraExtensions = append(template.ExtraExtensions, ctPoisonExt)
} else if len(req.sctList) > 0 {
if len(req.precertDER) == 0 {
return nil, nil, errors.New("inconsistent request contains sctList but no precertDER")
}
sctListExt, err := generateSCTListExt(req.sctList)
if err != nil {
return nil, nil, err
}
template.ExtraExtensions = append(template.ExtraExtensions, sctListExt)
} else {
return nil, nil, errors.New("invalid request contains neither sctList nor precertDER")
}
}

// Pick a CRL shard based on the serial number modulo the number of shards.
Expand Down
69 changes: 69 additions & 0 deletions issuance/cert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,75 @@ func TestIssueCommonName(t *testing.T) {
test.AssertEquals(t, cert.Subject.CommonName, "")
}

func TestPrepareMTC(t *testing.T) {
fc := clock.NewFake()
fc.Set(time.Now())

pc := defaultProfileConfig()
pc.MTC = true
pc.IgnoredLints = []string{
// Reduce the lint ignores to just the minimal (SCT-related) set.
"w_ct_sct_policy_count_unsatisfied",
// 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)
if err != nil {
t.Fatal(err)
}

signer, err := newIssuer(defaultIssuerConfig(), issuerCert, issuerSigner, fc)
if err != nil {
t.Fatal(err)
}

pk, err := ecdsa.GenerateKey(elliptic.P256(), nil)
if err != nil {
t.Fatal(err)
}

_, _, err = signer.Prepare(prof, &IssuanceRequest{
IncludeCTPoison: false,
sctList: nil,

PublicKey: MarshalablePublicKey{pk.Public()},
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),
})
if err != nil {
t.Errorf("Prepare() of req with !IncludeCTPoison && !sctList: %s", err)
}

_, _, err = signer.Prepare(prof, &IssuanceRequest{
IncludeCTPoison: true,

PublicKey: MarshalablePublicKey{pk.Public()},
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),
})
if err == nil {
t.Errorf("Prepare() of req with IncludeCTPoison: got nil err, want error")
}

_, _, err = signer.Prepare(prof, &IssuanceRequest{
sctList: []ct.SignedCertificateTimestamp{{SCTVersion: 1}},

PublicKey: MarshalablePublicKey{pk.Public()},
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),
})
if err == nil {
t.Errorf("Prepare() of req with sctList: got nil err, want error")
}
}

func TestIssueOmissions(t *testing.T) {
fc := clock.NewFake()
fc.Set(time.Now())
Expand Down