aboutsummaryrefslogtreecommitdiff
path: root/snapshot.go
diff options
context:
space:
mode:
Diffstat (limited to 'snapshot.go')
-rw-r--r--snapshot.go163
1 files changed, 0 insertions, 163 deletions
diff --git a/snapshot.go b/snapshot.go
deleted file mode 100644
index 5a9c50e..0000000
--- a/snapshot.go
+++ /dev/null
@@ -1,163 +0,0 @@
-package main
-
-import (
- "bytes"
- "context"
- "crypto/sha256"
- "crypto/x509"
- "encoding/json"
- "errors"
- "fmt"
- logger "log"
- "net/http"
- "os"
- "time"
-
- "git.cs.kau.se/rasmoste/ct-sans/internal/merkle"
- ct "github.com/google/certificate-transparency-go"
- "github.com/google/certificate-transparency-go/client"
- "github.com/google/certificate-transparency-go/jsonclient"
- "gitlab.torproject.org/rgdd/ct/pkg/metadata"
-)
-
-func snapshot(opts options) error {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
- if err := os.MkdirAll(opts.Directory, os.ModePerm); err != nil {
- return err
- }
-
- logger.Printf("INFO: updating metadata file\n")
- source := metadata.NewHTTPSource(metadata.HTTPSourceOptions{Name: "google"})
- msg, sig, md, err := source.Load(ctx)
- if err != nil {
- return err
- }
- if err := os.WriteFile(fmt.Sprintf("%s/%s", opts.Directory, opts.metadataFile), msg, 0644); err != nil {
- return err
- }
- if err := os.WriteFile(fmt.Sprintf("%s/%s", opts.Directory, opts.metadataSignatureFile), sig, 0644); err != nil {
- return err
- }
- timestamp := []byte(fmt.Sprintf("%d", time.Now().Unix()))
- if err := os.WriteFile(fmt.Sprintf("%s/%s", opts.Directory, opts.metadataTimestampFile), timestamp, 0644); err != nil {
- return err
- }
-
- logger.Printf("INFO: updating signed tree heads\n")
- for _, log := range logs(md) {
- id, _ := log.Key.ID()
- der, _ := x509.MarshalPKIXPublicKey(log.Key)
- dir := fmt.Sprintf("%s/%x", opts.logDirectory, id)
- sthFile := fmt.Sprintf("%s/%s", dir, opts.sthFile)
- if err := os.MkdirAll(dir, os.ModePerm); err != nil {
- return fmt.Errorf("%s: %v", log.Description, err)
- }
-
- // Fetch next STH
- cli, err := client.New(string(log.URL), &http.Client{}, jsonclient.Options{PublicKeyDER: der, UserAgent: opts.HTTPAgent})
- if err != nil {
- return fmt.Errorf("%s: %v", *log.Description, err)
- }
- nextSTH, err := cli.GetSTH(ctx)
- if err != nil {
- return fmt.Errorf("%s: %v", *log.Description, err)
- }
- nextSTHBytes, err := json.Marshal(nextSTH)
- if err != nil {
- return fmt.Errorf("%s: %v", *log.Description, err)
- }
- //
- // It's a bit ugly that ct.SignedTreeHead contains fields that
- // are not populated. Doesn't cause any prolems here, however.
- //
-
- // Bootstrap log if we don't have any STH yet
- if _, err := os.Stat(sthFile); err != nil {
- if !errors.Is(err, os.ErrNotExist) {
- return fmt.Errorf("%s: %v", *log.Description, err)
- }
- if err := os.WriteFile(sthFile, nextSTHBytes, 0644); err != nil {
- return fmt.Errorf("%s: %v", *log.Description, err)
- }
-
- logger.Printf("INFO: bootstrapped %s at tree size %d\n", *log.Description, nextSTH.TreeSize)
- continue
- }
-
- // Otherwise: update an existing STH
- currSTHBytes, err := os.ReadFile(sthFile)
- if err != nil {
- return fmt.Errorf("%s: %v", *log.Description, err)
- }
- var currSTH ct.SignedTreeHead
- if err := json.Unmarshal(currSTHBytes, &currSTH); err != nil {
- return fmt.Errorf("%s: %v", *log.Description, err)
- }
- if nextSTH.TreeSize < currSTH.TreeSize {
- return fmt.Errorf("%s: next tree size is smaller: %s", *log.Description, nextSTHBytes)
- }
- if nextSTH.TreeSize == currSTH.TreeSize {
- if !bytes.Equal(nextSTH.SHA256RootHash[:], currSTH.SHA256RootHash[:]) {
- return fmt.Errorf("%s: split-view: %s", *log.Description, nextSTHBytes)
- }
-
- logger.Printf("INFO: %s is already up-to-date at size %d\n", *log.Description, nextSTH.TreeSize)
- continue
- }
- hashes, err := cli.GetSTHConsistency(ctx, currSTH.TreeSize, nextSTH.TreeSize)
- if err != nil {
- return fmt.Errorf("%s: %v", *log.Description, err)
- }
- if err := merkle.VerifyConsistency(currSTH.TreeSize,
- nextSTH.TreeSize,
- [sha256.Size]byte(currSTH.SHA256RootHash),
- [sha256.Size]byte(nextSTH.SHA256RootHash),
- proof(hashes)); err != nil {
- return fmt.Errorf("%s: inconsistent tree: %v", *log.Description, err)
- }
- if err := os.WriteFile(sthFile, nextSTHBytes, 0644); err != nil {
- return fmt.Errorf("%s: %v", *log.Description, err)
- }
- logger.Printf("INFO: updated %s to tree size %d\n", *log.Description, nextSTH.TreeSize)
- }
- return nil
-}
-
-// logs select logs that count towards CT-compliance checks. Logs that don't
-// have a description are skipped after printing a warning.
-func logs(md metadata.Metadata) (logs []metadata.Log) {
- for _, operators := range md.Operators {
- for _, log := range operators.Logs {
- if log.Description == nil {
- fmt.Fprintf(os.Stderr, "WARNING: skipping log without description")
- continue
- }
- if log.State == nil {
- continue // skip logs with unknown states
- }
- if log.State.Name == metadata.LogStatePending {
- continue // pending logs do not count towards CT-compliance
- }
- if log.State.Name == metadata.LogStateRetired {
- continue // retired logs are not necessarily reachable
- }
- if log.State.Name == metadata.LogStateRejected {
- continue // rejected logs do not count towards CT-compliance
- }
-
- logs = append(logs, log)
- }
- }
- return
-}
-
-// proof formats hashes so that they can be passed to the merkle package
-func proof(hashes [][]byte) (p [][sha256.Size]byte) {
- for _, hash := range hashes {
- var h [sha256.Size]byte
- copy(h[:], hash)
- p = append(p, h)
- }
- return
-}