diff options
author | Ben Burwell <ben@benburwell.com> | 2019-09-09 09:28:43 -0400 |
---|---|---|
committer | Ben Burwell <ben@benburwell.com> | 2019-09-09 09:28:43 -0400 |
commit | 81342fc5250e14a53b42cb168f7fd6fffad50de5 (patch) | |
tree | 19401ed80d2c2ad943eabf67bc549731ddf4d3b1 |
initial commit
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | go.mod | 5 | ||||
-rw-r--r-- | go.sum | 9 | ||||
-rw-r--r-- | main.go | 96 |
4 files changed, 111 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fbfcc9f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +gosumdbaudit @@ -0,0 +1,5 @@ +module git.sr.ht/~benburwell/gosumdbaudit + +go 1.13 + +require golang.org/x/mod v0.1.0 @@ -0,0 +1,9 @@ +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/mod v0.1.0 h1:sfUMP1Gu8qASkorDVjnMuvgJzwFbTZSeXFiGBYAVdl4= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -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 +} |