aboutsummaryrefslogtreecommitdiff
path: root/pkg/storage/index/inmem.go
blob: 0a084bf70d0be7a619cfad3fa6790165dbc51a14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package index

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/google/certificate-transparency-go/x509"
	"rgdd.se/silent-ct/pkg/crtutil"
)

type CertificateID string

func (crtID *CertificateID) Set(crt x509.Certificate) {
	*crtID = CertificateID(crtutil.UniqueID(crt))
}

type CertificateInfo struct {
	ObservedAt time.Time `json:"observed_at"`
	StoredAt   string    `json:"stored_at"`
}

// index is an in-memory index of certificates
type index struct {
	Alerting   map[CertificateID][]CertificateInfo `json:"alerting"`   // Certificates that were not marked as "good" on time
	Legitimate map[CertificateID][]CertificateInfo `json:"legitimate"` // Certificates that are considered "good"
	Pending    map[CertificateID][]CertificateInfo `json:"pending"`    // Certificates that have yet to be marked as "good"
}

func newIndex() index {
	return index{
		Alerting:   make(map[CertificateID][]CertificateInfo),
		Legitimate: make(map[CertificateID][]CertificateInfo),
		Pending:    make(map[CertificateID][]CertificateInfo),
	}
}

func (ix *index) JSONUnmarshal(b []byte) error {
	type internal index
	if err := json.Unmarshal(b, (*internal)(ix)); err != nil {
		return err
	}
	for i, m := range []map[CertificateID][]CertificateInfo{ix.Alerting, ix.Legitimate, ix.Pending} {
		if m == nil {
			return fmt.Errorf("dictionary named %q is not in the index", []string{"alerting", "legitimate", "pending"}[i])
		}
	}
	return nil
}

func (ix *index) triggerAlerts(delay time.Duration) []CertificateInfo {
	var alerts []CertificateInfo

	for key, certInfos := range ix.Pending {
		certInfo := certInfos[0]
		if time.Since(certInfo.ObservedAt) < delay {
			continue
		}

		alerts = append(alerts, certInfo)
		ix.Alerting[key] = certInfos
		delete(ix.Pending, key)
	}

	return alerts
}

func (ix *index) addChain(crtID CertificateID, path string) bool {
	if _, ok := ix.Legitimate[crtID]; ok {
		return false // we already marked this certificate as "good"
	}

	entry := CertificateInfo{ObservedAt: time.Now(), StoredAt: path}
	crtInfos := []CertificateInfo{entry}
	if v, ok := ix.Alerting[crtID]; ok {
		crtInfos = append(crtInfos, v...)
		delete(ix.Alerting, crtID) // no longer alerting
	} else if v, ok := ix.Pending[crtID]; ok {
		crtInfos = append(crtInfos, v...)
		delete(ix.Pending, crtID) // no longer pending
	}

	ix.Legitimate[crtID] = crtInfos
	return true // index updated such that this certificate is marked as "good"
}

func (ix *index) addEntry(crtID CertificateID, path string) bool {
	crtInfo := CertificateInfo{ObservedAt: time.Now(), StoredAt: path}
	if _, ok := ix.Legitimate[crtID]; ok {
		return add(ix.Legitimate, crtID, crtInfo)
	} else if _, ok := ix.Alerting[crtID]; ok {
		return add(ix.Alerting, crtID, crtInfo)
	}
	return add(ix.Pending, crtID, crtInfo)
}

func add(m map[CertificateID][]CertificateInfo, key CertificateID, value CertificateInfo) bool {
	crtInfos, ok := m[key]
	if !ok {
		m[key] = []CertificateInfo{value}
		return true
	}

	for _, crtInfo := range crtInfos {
		if value.StoredAt == crtInfo.StoredAt {
			return false // duplicate
		}
	}

	crtInfos = append(crtInfos, value)
	m[key] = crtInfos
	return true
}