...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package ctpolicy
17
18 import (
19 "fmt"
20 "sync"
21
22 "github.com/google/certificate-transparency-go/loglist3"
23 "github.com/google/certificate-transparency-go/x509"
24 )
25
26 const (
27
28 BaseName = "All-logs"
29 )
30
31
32 type LogGroupInfo struct {
33 Name string
34 LogURLs map[string]bool
35 MinInclusions int
36 IsBase bool
37 LogWeights map[string]float32
38 wMu sync.RWMutex
39 }
40
41 func (group *LogGroupInfo) setMinInclusions(i int) error {
42 if i < 0 {
43 return fmt.Errorf("cannot assign negative minimal inclusions number")
44 }
45
46 group.MinInclusions = i
47 if i > len(group.LogURLs) {
48 return fmt.Errorf("trying to assign %d minimal inclusion number while only %d logs are part of group %q", i, len(group.LogURLs), group.Name)
49 }
50 return nil
51 }
52
53 func (group *LogGroupInfo) populate(ll *loglist3.LogList, included func(op *loglist3.Operator) bool) {
54 group.LogURLs = make(map[string]bool)
55 group.LogWeights = make(map[string]float32)
56 for _, op := range ll.Operators {
57 if included(op) {
58 for _, l := range op.Logs {
59 group.LogURLs[l.URL] = true
60 group.LogWeights[l.URL] = 1.0
61 }
62 }
63 }
64 }
65
66
67
68 func (group *LogGroupInfo) satisfyMinimalInclusion(weights map[string]float32) bool {
69 nonZeroNum := 0
70 for logURL, w := range weights {
71 if group.LogURLs[logURL] && w > 0.0 {
72 nonZeroNum++
73 if nonZeroNum >= group.MinInclusions {
74 return true
75 }
76 }
77 }
78 return false
79 }
80
81
82
83
84 func (group *LogGroupInfo) SetLogWeights(weights map[string]float32) error {
85 for logURL, w := range weights {
86 if w < 0.0 {
87 return fmt.Errorf("trying to assign negative weight %v to Log %q", w, logURL)
88 }
89 }
90 if !group.satisfyMinimalInclusion(weights) {
91 return fmt.Errorf("trying to assign weights %v resulting in inability to reach minimal inclusion number %d", weights, group.MinInclusions)
92 }
93 group.wMu.Lock()
94 defer group.wMu.Unlock()
95
96 for logURL := range group.LogURLs {
97 group.LogWeights[logURL] = 0.0
98 }
99 for logURL, w := range weights {
100 if group.LogURLs[logURL] {
101 group.LogWeights[logURL] = w
102 }
103 }
104 return nil
105 }
106
107
108
109
110 func (group *LogGroupInfo) SetLogWeight(logURL string, w float32) error {
111 if !group.LogURLs[logURL] {
112 return fmt.Errorf("trying to assign weight to Log %q not belonging to the group", logURL)
113 }
114 if w < 0.0 {
115 return fmt.Errorf("trying to assign negative weight %v to Log %q", w, logURL)
116 }
117 newWeights := make(map[string]float32)
118 for l, wt := range group.LogWeights {
119 newWeights[l] = wt
120 }
121 newWeights[logURL] = w
122 if !group.satisfyMinimalInclusion(newWeights) {
123 return fmt.Errorf("assigning weight %v to Log %q will result in inability to reach minimal inclusion number %d", w, logURL, group.MinInclusions)
124 }
125 group.wMu.Lock()
126 defer group.wMu.Unlock()
127 group.LogWeights = newWeights
128 return nil
129 }
130
131
132
133 func (group *LogGroupInfo) GetSubmissionSession() []string {
134 if len(group.LogURLs) == 0 {
135 return make([]string, 0)
136 }
137 session := make([]string, 0)
138
139
140 unProcessedWeights := make(map[string]float32)
141 for logURL, w := range group.LogWeights {
142 unProcessedWeights[logURL] = w
143 }
144
145 group.wMu.RLock()
146 defer group.wMu.RUnlock()
147 for range group.LogURLs {
148 sampleLog, err := weightedRandomSample(unProcessedWeights)
149 if err != nil {
150
151 return session
152 }
153 session = append(session, sampleLog)
154 delete(unProcessedWeights, sampleLog)
155 }
156 return session
157 }
158
159
160
161 type LogPolicyData map[string]*LogGroupInfo
162
163
164
165 func (groups LogPolicyData) TotalLogs() int {
166 unifiedLogs := make(map[string]bool)
167 for _, g := range groups {
168 if g.IsBase {
169 return len(g.LogURLs)
170 }
171 for l := range g.LogURLs {
172 unifiedLogs[l] = true
173 }
174 }
175 return len(unifiedLogs)
176 }
177
178
179
180 type CTPolicy interface {
181
182
183 LogsByGroup(cert *x509.Certificate, approved *loglist3.LogList) (LogPolicyData, error)
184 Name() string
185 }
186
187
188 func BaseGroupFor(approved *loglist3.LogList, incCount int) (*LogGroupInfo, error) {
189 baseGroup := LogGroupInfo{Name: BaseName, IsBase: true}
190 baseGroup.populate(approved, func(op *loglist3.Operator) bool { return true })
191 err := baseGroup.setMinInclusions(incCount)
192 return &baseGroup, err
193 }
194
195
196
197 func lifetimeInMonths(cert *x509.Certificate) int {
198 startYear, startMonth, startDay := cert.NotBefore.Date()
199 endYear, endMonth, endDay := cert.NotAfter.Date()
200 lifetimeInMonths := (int(endYear)-int(startYear))*12 + (int(endMonth) - int(startMonth))
201 if endDay < startDay {
202
203 lifetimeInMonths--
204 }
205 return lifetimeInMonths
206 }
207
208
209 type GroupSet map[string]bool
210
211
212
213 func GroupByLogs(lg LogPolicyData) map[string]GroupSet {
214 result := make(map[string]GroupSet)
215 for groupname, g := range lg {
216 for logURL := range g.LogURLs {
217 if _, seen := result[logURL]; !seen {
218 result[logURL] = make(GroupSet)
219 }
220 result[logURL][groupname] = true
221 }
222 }
223 return result
224 }
225
View as plain text