aboutsummaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'main.go')
-rw-r--r--main.go96
1 files changed, 96 insertions, 0 deletions
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
+}