Skip to content

Commit 42e74fc

Browse files
authored
require timestamping leaf certificate to be an end-entity (#1416)
Signed-off-by: Javid Khan <dxbjavid@gmail.com>
1 parent 3f20613 commit 42e74fc

4 files changed

Lines changed: 51 additions & 0 deletions

File tree

pkg/verification/verify.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ func verifyLeafCert(leafCert *x509.Certificate, verifiedChains [][]*x509.Certifi
111111

112112
errMsg := "failed to verify TSA certificate"
113113

114+
// The TSA certificate must be an end-entity certificate, per RFC 3161 2.3.
115+
// crypto/x509 chain verification does not reject a CA certificate used as
116+
// the leaf, so a certificate that asserts the CA bit yet carries the
117+
// timestamping EKU would otherwise be accepted as a signer.
118+
if leafCert.IsCA {
119+
return fmt.Errorf("%s: %w", errMsg, errors.New("TSA certificate must be an end-entity certificate, not a CA"))
120+
}
121+
114122
err := verifyLeafCertCriticalEKU(leafCert)
115123
if err != nil {
116124
return fmt.Errorf("%s: %w", errMsg, err)

pkg/verification/verify_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,36 @@ func TestVerifyLeafCert(t *testing.T) {
284284
}
285285
}
286286

287+
// A certificate that asserts the CA bit but carries the timestamping EKU must
288+
// not be accepted as a TSA leaf, per RFC 3161 2.3.
289+
func TestVerifyLeafCertRejectsCACertificate(t *testing.T) {
290+
criticalExtension := pkix.Extension{
291+
Id: EKUOID,
292+
Critical: true,
293+
}
294+
295+
caCert := &x509.Certificate{
296+
Raw: []byte("abc123"),
297+
RawIssuer: []byte("abc123"),
298+
SerialNumber: big.NewInt(int64(123)),
299+
Extensions: []pkix.Extension{criticalExtension},
300+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageTimeStamping},
301+
BasicConstraintsValid: true,
302+
IsCA: true,
303+
Subject: pkix.Name{
304+
CommonName: "TSA-Service",
305+
},
306+
}
307+
308+
err := verifyLeafCert(caCert, nil, VerifyOpts{})
309+
if err == nil {
310+
t.Fatal("expected verification to fail for a CA certificate used as a timestamping leaf")
311+
}
312+
if !strings.Contains(err.Error(), "end-entity") {
313+
t.Fatalf("expected an end-entity error, got %s", err.Error())
314+
}
315+
}
316+
287317
func TestVerifySubjectCommonName(t *testing.T) {
288318
type test struct {
289319
optsCommonName string

pkg/x509/x509.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ func VerifyCertChain(certs []*x509.Certificate, signer crypto.Signer, enforceInt
7777
}
7878
}
7979

80+
// Verify the leaf is an end-entity certificate, per RFC 3161 2.3. The chain
81+
// verification above does not reject a CA certificate used as the leaf.
82+
if leaf.IsCA {
83+
return errors.New("leaf certificate must be an end-entity certificate, not a CA")
84+
}
85+
8086
// Verify leaf has only a single EKU for timestamping, per RFC 3161 2.3
8187
// This should be enforced by Verify already. Key purposes that crypto/x509
8288
// does not recognise land in UnknownExtKeyUsage, so count those too.

pkg/x509/x509_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,11 @@ func TestVerifyCertChain(t *testing.T) {
6464
if err := VerifyCertChain([]*x509.Certificate{extraEKULeaf, subCert, rootCert}, extraEKUKey, true); err == nil || !strings.Contains(err.Error(), "should only contain one EKU") {
6565
t.Fatalf("expected failure verifying certificate chain with extra EKU: %v", err)
6666
}
67+
68+
// failure: leaf asserts the CA bit, so it is not an end-entity, per RFC 3161 2.3
69+
caLeaf, caLeafKey, _ := testutils.GenerateLeafCert(subCert, subKey)
70+
caLeaf.IsCA = true
71+
if err := VerifyCertChain([]*x509.Certificate{caLeaf, subCert, rootCert}, caLeafKey, true); err == nil || !strings.Contains(err.Error(), "end-entity") {
72+
t.Fatalf("expected failure verifying certificate chain with a CA leaf: %v", err)
73+
}
6774
}

0 commit comments

Comments
 (0)