diff options
author | Rasmus Dahlberg <rasmus@rgdd.se> | 2023-12-31 09:39:25 +0100 |
---|---|---|
committer | Rasmus Dahlberg <rasmus@rgdd.se> | 2024-01-07 20:22:23 +0100 |
commit | e18d36ebae30536c77c61cd5da123991e0ca1629 (patch) | |
tree | bf4880c0019a6009ab1b671e23ef4a1a4a5e8e08 /pkg/storage/index/index.go | |
parent | 54d980afcbd6f0011d6a162e0003587d26a3e311 (diff) |
Add drafty prototype
Diffstat (limited to 'pkg/storage/index/index.go')
-rw-r--r-- | pkg/storage/index/index.go | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/pkg/storage/index/index.go b/pkg/storage/index/index.go new file mode 100644 index 0000000..ef9ad60 --- /dev/null +++ b/pkg/storage/index/index.go @@ -0,0 +1,103 @@ +// Package index provides an index of locally stored certificates. If a method +// succeeds, the index and the data that it tracks has been persisted to disk. +// If a method does not succeed, restore from the persisted index on disk. +package index + +import ( + "crypto/sha256" + "fmt" + "time" + + "rgdd.se/silent-ct/internal/ioutil" + "rgdd.se/silent-ct/internal/monitor" + "rgdd.se/silent-ct/pkg/crtutil" +) + +type Config struct { + PermitBootstrap bool // Create a new index if a valid one does not exist on disk yet + IndexFile string // Path to an index file that can be read/written + TrustDirectory string // Absolute path to an existing directory where legitimate certificates are stored + MatchDirectory string // Absolute path to an existing directory where matching certificates are stored + + // Optional + AlertDelay time.Duration // Time before alerting on certificates that are unaccounted for +} + +type Index struct { + mem index + cfg Config +} + +func New(cfg Config) (Index, error) { + ix := Index{cfg: cfg} + if err := ioutil.DirectoriesExist([]string{cfg.TrustDirectory, cfg.MatchDirectory}); err != nil { + return Index{}, err + } + if err := ioutil.ReadJSON(cfg.IndexFile, &ix.mem); err != nil { + if !cfg.PermitBootstrap { + return Index{}, err + } + + ix.mem = newIndex() + if err := ioutil.CommitJSON(cfg.IndexFile, ix.mem); err != nil { + return Index{}, err + } + } + return ix, ix.Validate() +} + +func (ix *Index) AddChain(node string, pem []byte) error { + chain, err := crtutil.CertificateChainFromPEM(pem) + if err != nil { + return err + } + + var crtID CertificateID + crtID.Set(chain[0]) + path := fmt.Sprintf("%s/%s-%s.pem", ix.cfg.TrustDirectory, node, crtID) + if !ix.mem.addChain(crtID, path) { + return nil // duplicate + } + + if ioutil.CommitData(path, pem); err != nil { + return err + } + return ioutil.CommitJSON(ix.cfg.IndexFile, ix.mem) +} + +func (ix *Index) AddEntries(logID [sha256.Size]byte, entries []monitor.LogEntry) error { + addEntry := func(entry monitor.LogEntry) error { + crt, err := crtutil.CertificateFromLogEntry(entry.LeafData, entry.ExtraData) + if err != nil { + return err + } + + var crtID CertificateID + crtID.Set(crt) + path := fmt.Sprintf("%s/%x-%d.json", ix.cfg.MatchDirectory, logID[:], entry.LeafIndex) + if !ix.mem.addEntry(crtID, path) { + return nil // duplicate + } + + return ioutil.CommitJSON(path, entry) + } + + for _, entry := range entries { + if err := addEntry(entry); err != nil { + return err + } + } + return ioutil.CommitJSON(ix.cfg.IndexFile, ix.mem) +} + +func (ix *Index) TriggerAlerts() ([]CertificateInfo, error) { + alerts := ix.mem.triggerAlerts(ix.cfg.AlertDelay) + if len(alerts) == 0 { + return []CertificateInfo{}, nil + } + return alerts, ioutil.CommitJSON(ix.cfg.IndexFile, ix.mem) +} + +func (index *Index) Validate() error { + return nil // FIXME: check that the index is populated with valid values +} |