diff options
-rw-r--r-- | database.go | 75 | ||||
-rw-r--r-- | main.go | 46 | ||||
-rw-r--r-- | monitor/database.go | 13 | ||||
-rw-r--r-- | own.go | 155 |
4 files changed, 12 insertions, 277 deletions
diff --git a/database.go b/database.go deleted file mode 100644 index 87dfb4c..0000000 --- a/database.go +++ /dev/null @@ -1,75 +0,0 @@ -package main - -import ( - "bytes" - "io/ioutil" - "log" - "net/http" - "sync" - "time" - - "git.sr.ht/~benburwell/gosumdbaudit/sumdb" -) - -type database struct { - host string - key string - pollInterval time.Duration - - hc http.Client - - config map[string][]byte - configMu sync.RWMutex -} - -func (d *database) ReadRemote(path string) ([]byte, error) { - log.Printf("read remote: %s", path) - resp, err := d.hc.Get("https://" + d.host + path) - if err != nil { - return nil, err - } - defer resp.Body.Close() - return ioutil.ReadAll(resp.Body) -} - -func (d *database) ReadConfig(file string) ([]byte, error) { - log.Printf("read config: %s", file) - if file == "key" { - return []byte(d.key), nil - } - d.configMu.RLock() - defer d.configMu.RUnlock() - if d.config == nil { - d.config = make(map[string][]byte) - // d.config["sum.golang.org/latest"] = []byte(`go.sum database tree - // 163038 - // S1dhskM/kuUJUOCz3InBRhl0vFiHxr0INft+24ClisI= - - // — sum.golang.org Az3gruAGD/ybzwcCUArmKpzAZNmEOu3Yahr9WIKA2SFAK3G2xzo39uHS70mylR3nsT9t3ZpVQW89RT6Tg1+1nIf7bgI= - // `) - } - c, ok := d.config[file] - if !ok { - return nil, nil - } - return c, nil -} - -func (d *database) WriteConfig(file string, old, new []byte) error { - log.Printf("write config: %s", file) - d.configMu.Lock() - defer d.configMu.Unlock() - if val, ok := d.config[file]; ok && !bytes.Equal(val, old) { - return sumdb.ErrWriteConflict - } - d.config[file] = new - return nil -} - -func (d *database) Log(msg string) { - log.Printf(msg) -} - -func (d *database) SecurityError(msg string) { - log.Printf("!!! SECURITY ERROR !!!\n%s", msg) -} @@ -14,51 +14,7 @@ func main() { Key: "sum.golang.org+033de0ae+Ac4zctda0e5eza+HJyk9SxEdh+s3Ux18htTTAD8OuAn8", }) - if err := mon.Watch(10 * time.Second); err != nil { + if err := mon.Watch(1 * time.Minute); err != nil { log.Printf("AUDIT FAILED: %v", err) } } - -// func monitor(db *database) error { -// log.Printf("starting monitor for %s", db.host) - -// client := sumdb.NewClient(db) - -// tree, err := client.FetchLatest() -// if err != nil { -// return err -// } -// log.Printf("got latest: N=%d, Hash=%s", tree.N, tree.Hash) - -// if err := client.FetchTreeProof(tree); err != nil { -// return err -// } - -// // fetch all entries in the tree according to the STH -// // entries := client.Entries(nil, latest) - -// // confirm the tree made from the entries produces the same hash as the STH -// // IF NOT: the server has signed invalid data - -// // prev := latest -// for { -// // await a new STH -// // prev = latest -// time.Sleep(db.pollInterval) -// log.Printf("checking %s for new STH...", db.host) -// // awaitNewSTH() - -// // latest, err := client.Latest() -// // if err != nil { -// // return err -// // } - -// // fetch all NEW entries between prev and latest -// // if unavailable for an extended period, this should be viewed as misbehavior -// // entries := client.Entries(prev, latest) - -// // fetch a consistency proof for the new STH with the previous STH -// // verify consistency proof -// // verify the new entries generate the corresponding elements in the consistency proof -// } -// } diff --git a/monitor/database.go b/monitor/database.go index 7a0ead1..1c2a913 100644 --- a/monitor/database.go +++ b/monitor/database.go @@ -10,14 +10,23 @@ import ( "golang.org/x/mod/sumdb/tlog" ) +const userAgent = "gosumdbaudit/1.0 (+https://bnbl.io/gosumdbaudit)" + type Database struct { URL string Key string + + c http.Client } func (db *Database) readRemote(path string) ([]byte, error) { log.Printf("GET %s", path) - resp, err := http.Get("https://" + db.URL + path) + req, err := http.NewRequest(http.MethodGet, "https://"+db.URL+path, nil) + if err != nil { + return nil, err + } + req.Header.Set("user-agent", userAgent) + resp, err := db.c.Do(req) if err != nil { return nil, err } @@ -30,7 +39,7 @@ func (db *Database) readRemote(path string) ([]byte, error) { defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { - return nil, fmt.Errorf("get %s: %v", err) + return nil, fmt.Errorf("get %s: %v", path, err) } return body, nil } @@ -1,155 +0,0 @@ -package main - -// 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 -// pollInterval time.Duration -// } - -// // 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 -// } - -// // bootstrapMonitor fetches and verifies the current tree starting from the -// // first log entry, and returns the current verified size and hash. -// func (d *db) bootstrapMonitor() (int, string, error) { -// log.Printf("bootstrapping monitor") -// log.Printf("TODO: implement fully") -// log.Printf("verified until size 163038") -// return 163038, "S1dhskM/kuUJUOCz3InBRhl0vFiHxr0INft+24ClisI=", nil - -// // TODO: implement - -// // 1. Fetch the current STH (section 4.3) -// // 2. Verify the STH signature -// // size, hash, err := d.getLatest() -// // if err != nil { -// // return err -// // } - -// // 3. Fetch all entries in the tree corresponding to the STH (section 4.6) - -// // 4. Confirm that the tree made from the fetched entries produces the same -// // hash as that in the STH. -// } - -// // monitor monitors the db to ensure it behaves correctly, using the algorithm -// // for CT logs specified in RFC 6952 section 3.5. -// func (d *db) monitor() error { -// log.Printf("starting monitor") -// size, hash, err := d.bootstrapMonitor() -// if err != nil { -// return err -// } -// log.Printf("successfully verified merkle tree proof until size %d and hash %s", size, hash) - -// // 5. Fetch the current STH (section 4.3). Repeat until the STH changes. -// // 6. Verify the STH signature. -// log.Printf("waiting for a tree size greater than %d", size) -// newSize, newHash, err := d.awaitNewSTH(size) -// if err != nil { -// return err -// } -// log.Printf("got new STH with size %d and hash %s", newSize, newHash) - -// // 7. Fetch all the new entries in the tree corresponding to the STH (section -// // 4.6). If they remain unavailable for an extended period, then this should -// // be viewed as misbehavior on the part of the log. - -// // 8. Fetch a consistency proof for the new STH with the previous STH -// // (section 4.4). - -// // 9. Verify the consistency proof. - -// // 10. Verify that the new entries generate the corresponding elements in the -// // consistency proof. - -// // 11. Go to step 5. - -// return nil -// } - -// // awaitNewSTH periodically checks and verifies the current STH. If the latest -// // tree size differs from the previous size, the new verified size and hash are -// // returned. -// func (d *db) awaitNewSTH(prevSize int) (int, string, error) { -// for { -// log.Printf("sleeping...") -// time.Sleep(d.pollInterval) -// log.Printf("checking latest tree size") -// size, hash, err := d.getLatest() -// if err != nil { -// return 0, "", err -// } -// if size < prevSize { -// return 0, "", fmt.Errorf("misbehaving log: latest log contains %d entries but previously reported %d", size, prevSize) -// } -// if size != prevSize { -// log.Printf("found a new STH (size=%d)", size) -// return size, hash, nil -// } -// log.Printf("tree sizes match") -// } -// } - -// // 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 -// } |