From 81342fc5250e14a53b42cb168f7fd6fffad50de5 Mon Sep 17 00:00:00 2001 From: Ben Burwell Date: Mon, 9 Sep 2019 09:28:43 -0400 Subject: initial commit --- main.go | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 main.go (limited to 'main.go') diff --git a/main.go b/main.go new file mode 100644 index 0000000..d08cdbd --- /dev/null +++ b/main.go @@ -0,0 +1,96 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "log" + "net/http" + "strconv" + "strings" + + "golang.org/x/mod/sumdb/note" +) + +func main() { + dbs := []*db{ + &db{host: "sum.golang.org", key: "sum.golang.org+033de0ae+Ac4zctda0e5eza+HJyk9SxEdh+s3Ux18htTTAD8OuAn8"}, + //&db{host: "sum.golang.org", key: "sum.golang.org+033de0ae+BADBADBADBADBADBADBADBADBADBADBADBADBADBADBA"}, + } + for _, d := range dbs { + if err := audit(d); err != nil { + log.Printf("AUDIT FAIL (%s): %s", d.host, err.Error()) + } + } +} + +func audit(d *db) error { + log.Printf("starting audit of %s...", d.host) + size, hash, err := d.getLatest() + if err != nil { + return err + } + log.Printf("db size %d", size) + log.Printf("db hash %s", hash) + return nil +} + +type db struct { + host string + key string +} + +// httpGet makes a GET request to the specified path of the database and +// returns a byte slice of the response body. +func (d *db) httpGet(path string) ([]byte, error) { + client := &http.Client{} + resp, err := client.Get("https://" + d.host + path) + if err != nil { + return nil, err + } + defer resp.Body.Close() + var body bytes.Buffer + if _, err := io.Copy(&body, resp.Body); err != nil { + return nil, fmt.Errorf("could not read response body: %w", err) + } + return body.Bytes(), nil +} + +// verifyNote takes a signed byte slice, verifies the signature against the +// db's public key. If successful, the note content is returned, otherwise, an +// error. +func (d *db) verifyNote(b []byte) (string, error) { + verifier, err := note.NewVerifier(d.key) + if err != nil { + return "", err + } + verifiers := note.VerifierList(verifier) + msg, err := note.Open(b, verifiers) + if err != nil { + return "", err + } + return msg.Text, nil +} + +// getLatest fetches and verifies the latest signed tree head hash and database +// size. +func (d *db) getLatest() (int, string, error) { + body, err := d.httpGet("/latest") + if err != nil { + return 0, "", fmt.Errorf("could not fetch latest: %w", err) + } + msg, err := d.verifyNote(body) + if err != nil { + return 0, "", fmt.Errorf("could not verify note: %w", err) + } + parts := strings.Split(msg, "\n") + if len(parts) != 4 { + return 0, "", fmt.Errorf("could not parse latest: expected %d lines but got %d", 4, len(parts)) + } + size, err := strconv.Atoi(parts[1]) + if err != nil { + return 0, "", fmt.Errorf("could not parse tree size: %w", err) + } + hash := parts[2] + return size, hash, nil +} -- cgit v1.2.3