Skip to content

Commit ee72274

Browse files
authored
Find a new home for some functions (#245)
* move entryFromChain to x509util # Conflicts: # internal/hammer/loadtest/workers.go # Conflicts: # internal/scti/handlers.go * move verifyAddChain * convert functions to methods
1 parent 537b8ca commit ee72274

File tree

7 files changed

+140
-218
lines changed

7 files changed

+140
-218
lines changed

internal/hammer/loadtest/workers.go

Lines changed: 1 addition & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ package loadtest
1616

1717
import (
1818
"context"
19-
"crypto/sha256"
2019
"crypto/x509"
2120
"encoding/base64"
2221
"encoding/json"
@@ -32,7 +31,6 @@ import (
3231
"github.com/transparency-dev/static-ct/internal/types/rfc6962"
3332
"github.com/transparency-dev/static-ct/internal/x509util"
3433
"github.com/transparency-dev/trillian-tessera/api/layout"
35-
"github.com/transparency-dev/trillian-tessera/ctonly"
3634
"k8s.io/klog/v2"
3735
)
3836

@@ -362,7 +360,7 @@ func (v *MMDVerifier) Run(ctx context.Context) {
362360
v.errChan <- fmt.Errorf("failed to parse certificates: %v", err)
363361
continue
364362
}
365-
entry, err := entryFromChain(certs, false, leafMMD.timestamp)
363+
entry, err := x509util.EntryFromChain(certs, false, leafMMD.timestamp)
366364
if err != nil {
367365
v.errChan <- fmt.Errorf("failed to create entry from chain: %v", err)
368366
continue
@@ -384,78 +382,3 @@ func (v *MMDVerifier) Kill() {
384382
v.cancel()
385383
}
386384
}
387-
388-
// entryFromChain generates an Entry from a chain and timestamp.
389-
// Copied from certificate-transparency-go/serialization.go and internal/scti/handlers.go.
390-
// TODO(phboneff): move in a different file maybe?
391-
func entryFromChain(chain []*x509.Certificate, isPrecert bool, timestamp uint64) (*ctonly.Entry, error) {
392-
leaf := ctonly.Entry{
393-
IsPrecert: isPrecert,
394-
Timestamp: timestamp,
395-
}
396-
397-
if len(chain) > 1 {
398-
issuersChain := make([][32]byte, len(chain)-1)
399-
for i, c := range chain[1:] {
400-
issuersChain[i] = sha256.Sum256(c.Raw)
401-
}
402-
leaf.FingerprintsChain = issuersChain
403-
}
404-
405-
if !isPrecert {
406-
leaf.Certificate = chain[0].Raw
407-
return &leaf, nil
408-
}
409-
410-
// Pre-certs are more complicated. First, parse the leaf pre-cert and its
411-
// putative issuer.
412-
if len(chain) < 2 {
413-
return nil, fmt.Errorf("no issuer cert available for precert leaf building")
414-
}
415-
issuer := chain[1]
416-
cert := chain[0]
417-
418-
var preIssuer *x509.Certificate
419-
if isPreIssuer(issuer) {
420-
// Replace the cert's issuance information with details from the pre-issuer.
421-
preIssuer = issuer
422-
423-
// The issuer of the pre-cert is not going to be the issuer of the final
424-
// cert. Change to use the final issuer's key hash.
425-
if len(chain) < 3 {
426-
return nil, fmt.Errorf("no issuer cert available for pre-issuer")
427-
}
428-
issuer = chain[2]
429-
}
430-
431-
// Next, post-process the DER-encoded TBSCertificate, to remove the CT poison
432-
// extension and possibly update the issuer field.
433-
defangedTBS, err := x509util.BuildPrecertTBS(cert.RawTBSCertificate, preIssuer)
434-
if err != nil {
435-
return nil, fmt.Errorf("failed to remove poison extension: %v", err)
436-
}
437-
438-
leaf.Precertificate = cert.Raw
439-
// TODO(phboneff): do we need this?
440-
// leaf.PrecertSigningCert = issuer.Raw
441-
leaf.Certificate = defangedTBS
442-
443-
issuerKeyHash := sha256.Sum256(issuer.RawSubjectPublicKeyInfo)
444-
leaf.IssuerKeyHash = issuerKeyHash[:]
445-
return &leaf, nil
446-
}
447-
448-
// isPreIssuer indicates whether a certificate is a pre-cert issuer with the specific
449-
// certificate transparency extended key usage.
450-
// Copied from certificate-transparency-go/serialization.go and internal/scti/handlers.go.
451-
// TODO(phboneff): unify these.
452-
func isPreIssuer(cert *x509.Certificate) bool {
453-
// Look for the extension in the Extensions field and not ExtKeyUsage
454-
// since crypto/x509 does not recognize this extension as an ExtKeyUsage.
455-
for _, ext := range cert.Extensions {
456-
if rfc6962.OIDExtKeyUsageCertificateTransparency.Equal(ext.Id) {
457-
return true
458-
}
459-
}
460-
return false
461-
}

internal/scti/chain_validation.go

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ func isPrecertificate(cert *x509.Certificate) (bool, error) {
149149
// supplied in the chain. Then applies the RFC requirement that the path must involve all
150150
// the submitted chain in the order of submission.
151151
// TODO(phboneff): make this a method func([][]byte) ([]*x509.Certificate, error)
152-
func validateChain(rawChain [][]byte, validationOpts ChainValidationOpts) ([]*x509.Certificate, error) {
152+
func (opts ChainValidationOpts) validateChain(rawChain [][]byte) ([]*x509.Certificate, error) {
153153
if len(rawChain) == 0 {
154154
return nil, errors.New("empty certificate chain")
155155
}
@@ -172,8 +172,8 @@ func validateChain(rawChain [][]byte, validationOpts ChainValidationOpts) ([]*x5
172172
}
173173
}
174174

175-
naStart := validationOpts.notAfterStart
176-
naLimit := validationOpts.notAfterLimit
175+
naStart := opts.notAfterStart
176+
naLimit := opts.notAfterLimit
177177
cert := chain[0]
178178

179179
// Check whether the expiry date of the cert is within the acceptable range.
@@ -184,24 +184,24 @@ func validateChain(rawChain [][]byte, validationOpts ChainValidationOpts) ([]*x5
184184
return nil, fmt.Errorf("certificate NotAfter (%v) >= %v", cert.NotAfter, *naLimit)
185185
}
186186

187-
now := validationOpts.currentTime
187+
now := opts.currentTime
188188
if now.IsZero() {
189189
now = time.Now()
190190
}
191191
expired := now.After(cert.NotAfter)
192-
if validationOpts.rejectExpired && expired {
192+
if opts.rejectExpired && expired {
193193
return nil, errors.New("rejecting expired certificate")
194194
}
195-
if validationOpts.rejectUnexpired && !expired {
195+
if opts.rejectUnexpired && !expired {
196196
return nil, errors.New("rejecting unexpired certificate")
197197
}
198198

199199
// Check for unwanted extension types, if required.
200200
// TODO(al): Refactor CertValidationOpts c'tor to a builder pattern and
201201
// pre-calc this in there
202-
if len(validationOpts.rejectExtIds) != 0 {
202+
if len(opts.rejectExtIds) != 0 {
203203
badIDs := make(map[string]bool)
204-
for _, id := range validationOpts.rejectExtIds {
204+
for _, id := range opts.rejectExtIds {
205205
badIDs[id.String()] = true
206206
}
207207
for idx, ext := range cert.Extensions {
@@ -214,9 +214,9 @@ func validateChain(rawChain [][]byte, validationOpts ChainValidationOpts) ([]*x5
214214

215215
// TODO(al): Refactor CertValidationOpts c'tor to a builder pattern and
216216
// pre-calc this in there too.
217-
if len(validationOpts.extKeyUsages) > 0 {
217+
if len(opts.extKeyUsages) > 0 {
218218
acceptEKUs := make(map[x509.ExtKeyUsage]bool)
219-
for _, eku := range validationOpts.extKeyUsages {
219+
for _, eku := range opts.extKeyUsages {
220220
acceptEKUs[eku] = true
221221
}
222222
good := false
@@ -227,7 +227,7 @@ func validateChain(rawChain [][]byte, validationOpts ChainValidationOpts) ([]*x5
227227
}
228228
}
229229
if !good {
230-
return nil, fmt.Errorf("rejecting certificate without EKU in %v", validationOpts.extKeyUsages)
230+
return nil, fmt.Errorf("rejecting certificate without EKU in %v", opts.extKeyUsages)
231231
}
232232
}
233233

@@ -237,9 +237,9 @@ func validateChain(rawChain [][]byte, validationOpts ChainValidationOpts) ([]*x5
237237
// - allow certificate without policing them since this is not CT's responsibility
238238
// See /internal/lax509/README.md for further information.
239239
verifyOpts := lax509.VerifyOptions{
240-
Roots: validationOpts.trustedRoots.CertPool(),
240+
Roots: opts.trustedRoots.CertPool(),
241241
Intermediates: intermediatePool.CertPool(),
242-
KeyUsages: validationOpts.extKeyUsages,
242+
KeyUsages: opts.extKeyUsages,
243243
}
244244

245245
verifiedChains, err := lax509.Verify(cert, verifyOpts)
@@ -263,6 +263,36 @@ func validateChain(rawChain [][]byte, validationOpts ChainValidationOpts) ([]*x5
263263
return nil, errors.New("no RFC compliant path to root found when trying to validate chain")
264264
}
265265

266+
// verifyAddChain is used by add-chain and add-pre-chain. It does the checks that the supplied
267+
// cert is of the correct type and chains to a trusted root.
268+
// TODO(phbnf): add tests
269+
func (opts ChainValidationOpts) verifyAddChain(req rfc6962.AddChainRequest, expectingPrecert bool) ([]*x509.Certificate, error) {
270+
// We already checked that the chain is not empty so can move on to verification
271+
validPath, err := opts.validateChain(req.Chain)
272+
if err != nil {
273+
// We rejected it because the cert failed checks or we could not find a path to a root etc.
274+
// Lots of possible causes for errors
275+
return nil, fmt.Errorf("chain failed to verify: %s", err)
276+
}
277+
278+
isPrecert, err := isPrecertificate(validPath[0])
279+
if err != nil {
280+
return nil, fmt.Errorf("precert test failed: %s", err)
281+
}
282+
283+
// The type of the leaf must match the one the handler expects
284+
if isPrecert != expectingPrecert {
285+
if expectingPrecert {
286+
klog.Warningf("Cert (or precert with invalid CT ext) submitted as precert chain: %q", req.Chain)
287+
} else {
288+
klog.Warningf("Precert (or cert with invalid CT ext) submitted as cert chain: %q", req.Chain)
289+
}
290+
return nil, fmt.Errorf("cert / precert mismatch: %T", expectingPrecert)
291+
}
292+
293+
return validPath, nil
294+
}
295+
266296
func chainsEquivalent(inChain []*x509.Certificate, verifiedChain []*x509.Certificate) bool {
267297
// The verified chain includes a root, but the input chain may or may not include a
268298
// root (RFC 6962 s4.1/ s4.2 "the last [certificate] is either the root certificate

internal/scti/chain_validation_test.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ func TestValidateChain(t *testing.T) {
254254
if !fakeCARoots.AppendCertsFromPEM([]byte(testdata.RealPrecertIntermediatePEM)) {
255255
t.Fatal("failed to load real intermediate")
256256
}
257-
validateOpts := ChainValidationOpts{
257+
opts := ChainValidationOpts{
258258
trustedRoots: fakeCARoots,
259259
}
260260

@@ -403,11 +403,11 @@ func TestValidateChain(t *testing.T) {
403403
}
404404
for _, test := range tests {
405405
t.Run(test.desc, func(t *testing.T) {
406-
validateOpts := validateOpts
406+
opts := opts
407407
if test.modifyOpts != nil {
408-
test.modifyOpts(&validateOpts)
408+
test.modifyOpts(&opts)
409409
}
410-
gotPath, err := validateChain(test.chain, validateOpts)
410+
gotPath, err := opts.validateChain(test.chain)
411411
if err != nil {
412412
if !test.wantErr {
413413
t.Errorf("ValidateChain()=%v,%v; want _,nil", gotPath, err)
@@ -433,7 +433,7 @@ func TestNotAfterRange(t *testing.T) {
433433
if !fakeCARoots.AppendCertsFromPEM([]byte(testdata.FakeCACertPEM)) {
434434
t.Fatal("failed to load fake root")
435435
}
436-
validateOpts := ChainValidationOpts{
436+
opts := ChainValidationOpts{
437437
trustedRoots: fakeCARoots,
438438
rejectExpired: false,
439439
}
@@ -473,12 +473,12 @@ func TestNotAfterRange(t *testing.T) {
473473
for _, test := range tests {
474474
t.Run(test.desc, func(t *testing.T) {
475475
if !test.notAfterStart.IsZero() {
476-
validateOpts.notAfterStart = &test.notAfterStart
476+
opts.notAfterStart = &test.notAfterStart
477477
}
478478
if !test.notAfterLimit.IsZero() {
479-
validateOpts.notAfterLimit = &test.notAfterLimit
479+
opts.notAfterLimit = &test.notAfterLimit
480480
}
481-
gotPath, err := validateChain(test.chain, validateOpts)
481+
gotPath, err := opts.validateChain(test.chain)
482482
if err != nil {
483483
if !test.wantErr {
484484
t.Errorf("ValidateChain()=%v,%v; want _,nil", gotPath, err)
@@ -500,7 +500,7 @@ func TestRejectExpiredUnexpired(t *testing.T) {
500500
}
501501
// Validity period: May 13, 2016 - Jul 12, 2019.
502502
chain := pemsToDERChain(t, []string{testdata.LeafSignedByFakeIntermediateCertPEM, testdata.FakeIntermediateCertPEM})
503-
validateOpts := ChainValidationOpts{
503+
opts := ChainValidationOpts{
504504
trustedRoots: fakeCARoots,
505505
extKeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
506506
}
@@ -587,10 +587,10 @@ func TestRejectExpiredUnexpired(t *testing.T) {
587587
},
588588
} {
589589
t.Run(tc.desc, func(t *testing.T) {
590-
validateOpts.currentTime = tc.now
591-
validateOpts.rejectExpired = tc.rejectExpired
592-
validateOpts.rejectUnexpired = tc.rejectUnexpired
593-
_, err := validateChain(chain, validateOpts)
590+
opts.currentTime = tc.now
591+
opts.rejectExpired = tc.rejectExpired
592+
opts.rejectUnexpired = tc.rejectUnexpired
593+
_, err := opts.validateChain(chain)
594594
if err != nil {
595595
if len(tc.wantErr) == 0 {
596596
t.Errorf("ValidateChain()=_,%v; want _,nil", err)
@@ -692,7 +692,7 @@ func TestPreIssuedCert(t *testing.T) {
692692
trustedRoots: roots,
693693
extKeyUsages: tc.eku,
694694
}
695-
chain, err := validateChain(rawChain, opts)
695+
chain, err := opts.validateChain(rawChain)
696696
if err != nil {
697697
t.Fatalf("failed to ValidateChain: %v", err)
698698
}

0 commit comments

Comments
 (0)