Skip to content

Commit 8b6cf9d

Browse files
authored
Check for discovered issuers concurrently with tree checking (#395)
1 parent b324b54 commit 8b6cf9d

1 file changed

Lines changed: 65 additions & 38 deletions

File tree

cmd/fsck/main.go

Lines changed: 65 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"crypto/sha256"
2121
"crypto/x509"
2222
"encoding/base64"
23+
"errors"
2324
"flag"
2425
"fmt"
2526
"net/http"
@@ -71,68 +72,94 @@ func main() {
7172
src.SetAuthorizationHeader(fmt.Sprintf("Bearer %s", *bearerToken))
7273
}
7374
v := verifierFromFlags()
74-
lsc := &logStateCollector{}
75-
if err := fsck.Check(ctx, *origin, v, src, *N, lsc.merkleLeafHasher()); err != nil {
76-
klog.Exitf("fsck failed: %v", err)
77-
}
75+
lsc := newLogStateCollector()
76+
eg := errgroup.Group{}
77+
eg.Go(func() error {
78+
defer lsc.Close()
79+
return fsck.Check(ctx, *origin, v, src, *N, lsc.merkleLeafHasher())
80+
81+
})
82+
eg.Go(func() error {
83+
return lsc.checkIssuersTask(ctx, src.ReadIssuer, *N)
84+
})
7885

79-
if err := checkIntermediates(ctx, lsc, src.ReadIssuer, *N); err != nil {
80-
klog.Exitf("Failed to verify presence intermediates: %v", err)
86+
if err := eg.Wait(); err != nil {
87+
klog.Exitf("FAILED:\n%v", err)
8188
}
8289

8390
klog.Info("OK")
8491
}
8592

86-
func checkIntermediates(ctx context.Context, lsc *logStateCollector, readIssuer func(context.Context, []byte) ([]byte, error), N uint) error {
87-
klog.Infof("Checking intermediates CAS")
88-
n := 0
89-
work := make(chan []byte, N)
90-
eg := errgroup.Group{}
93+
// logStateCollector tracks state of the target log which needs to be later checked.
94+
//
95+
// Currently, this is centred around the discovery and checking of issuers during entry bundle parsing.
96+
type logStateCollector struct {
97+
// issuersSeen contains the set of issuer fingerprints issuersSeen in the log so far.
98+
issuersSeen sync.Map
99+
// issuersToCheck is a channel of issuer fingerprints to look up in the target log's issuer CAS.
100+
issuersToCheck chan []byte
101+
}
102+
103+
// newLogStateCollector creates a new logStateCollector instance.
104+
func newLogStateCollector() *logStateCollector {
105+
return &logStateCollector{
106+
issuersToCheck: make(chan []byte),
107+
}
108+
}
109+
110+
// Close should be called once no further issuers will be added.
111+
func (l *logStateCollector) Close() {
112+
close(l.issuersToCheck)
113+
}
114+
115+
// checkIssuersTask reads looks up discovered issuer fingerprints in the target log's issuer CAS.
116+
//
117+
// This is a long-running function, it will only exit once Close has been called, and all remaining fingerprints in the
118+
// issuersToCheck channel have been checked.
119+
func (l *logStateCollector) checkIssuersTask(ctx context.Context, readIssuer func(context.Context, []byte) ([]byte, error), N uint) error {
120+
klog.Infof("Checking issuers CAS")
121+
errC := make(chan error)
122+
wg := sync.WaitGroup{}
91123
for range N {
92-
eg.Go(func() error {
93-
for fp := range work {
124+
wg.Add(1)
125+
go func() {
126+
defer wg.Done()
127+
for fp := range l.issuersToCheck {
94128
if _, err := readIssuer(ctx, fp); err != nil {
95-
return fmt.Errorf("couldn't fetch issuer for %x: %v", fp, err)
129+
klog.Warningf("Couldn't fetch issuer FP %x: %v", fp, err)
130+
errC <- fmt.Errorf("couldn't fetch issuer for %x: %v", fp, err)
131+
continue
96132
}
133+
klog.V(2).Infof("Issuer FP %x is present", fp)
97134
}
98-
return nil
99-
})
135+
}()
100136
}
137+
wg.Wait()
138+
close(errC)
101139

102-
lsc.intermediates.Range(func(k any, v any) bool {
103-
fp := k.(string)
104-
work <- []byte(fp)
105-
n++
106-
return true
107-
})
108-
close(work)
109-
110-
if err := eg.Wait(); err != nil {
111-
return fmt.Errorf("failed to fetch one or more issuers: %v", err)
140+
errs := []error{}
141+
for e := range errC {
142+
errs = append(errs, e)
112143
}
113-
114-
klog.Infof("Checked %d intermediate issuers", n)
115-
return nil
116-
}
117-
118-
type logStateCollector struct {
119-
intermediates sync.Map
144+
return errors.Join(errs...)
120145
}
121146

122-
func (l *logStateCollector) addIntermediates(fpRaw cryptobyte.String) {
147+
// addIssuers adds the issuers in the provided byte string to the set of issuer to be checked.
148+
func (l *logStateCollector) addIssuers(fpRaw cryptobyte.String) {
123149
var fp []byte
124150
for len(fpRaw) > 0 {
125151
fp, fpRaw = fpRaw[:32], fpRaw[32:]
126-
_, existed := l.intermediates.LoadOrStore(string(fp), true)
152+
_, existed := l.issuersSeen.LoadOrStore(string(fp), true)
127153
if !existed {
128-
klog.V(2).Infof("Found intermediate FP %x", fp)
154+
klog.V(2).Infof("Found issuer FP %x", fp)
155+
l.issuersToCheck <- fp
129156
}
130157
}
131158
}
132159

133160
// merkleLeafHasher returns a function which knows how to:
134161
// - calculate RFC6962 Merkle leaf hashes for entries in a Static-CT formatted entry bundle,
135-
// - keep track of the set of intermediate cert fingerprints seen while parsing entry bundles.
162+
// - keep track of the set of issuer cert fingerprints seen while parsing entry bundles.
136163
func (l *logStateCollector) merkleLeafHasher() func(bundle []byte) ([][]byte, error) {
137164
return func(bundle []byte) ([][]byte, error) {
138165
r := make([][]byte, 0, layout.EntryBundleWidth)
@@ -187,7 +214,7 @@ func (l *logStateCollector) merkleLeafHasher() func(bundle []byte) ([][]byte, er
187214
if !b.ReadUint16LengthPrefixed(&fpRaw) {
188215
return nil, fmt.Errorf("failed to read chain fingerprints at entry index %d of bundle", i)
189216
}
190-
l.addIntermediates(fpRaw)
217+
l.addIssuers(fpRaw)
191218

192219
h := rfc6962.DefaultHasher.HashLeaf(preimage.BytesOrPanic())
193220
r = append(r, h)

0 commit comments

Comments
 (0)