1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package submission
16
17 import (
18 "context"
19 "regexp"
20 "sync"
21 "testing"
22 "time"
23
24 ct "github.com/google/certificate-transparency-go"
25 "github.com/google/certificate-transparency-go/ctpolicy"
26 "github.com/google/certificate-transparency-go/testdata"
27 "github.com/google/certificate-transparency-go/tls"
28 )
29
30 func testdataSCT() *ct.SignedCertificateTimestamp {
31 var sct ct.SignedCertificateTimestamp
32 if _, err := tls.Unmarshal(testdata.TestPreCertProof, &sct); err != nil {
33 panic(err)
34 }
35 return &sct
36 }
37
38 type mockSubmitter struct {
39 fixedDelay map[byte]time.Duration
40 firstLetterURLReqNumber map[byte]int
41 mu sync.Mutex
42 }
43
44
45 func (ms *mockSubmitter) SubmitToLog(_ context.Context, logURL string, _ []ct.ASN1Cert, _ bool) (*ct.SignedCertificateTimestamp, error) {
46 ms.mu.Lock()
47 reqNum := ms.firstLetterURLReqNumber[logURL[0]]
48 ms.firstLetterURLReqNumber[logURL[0]]++
49 ms.mu.Unlock()
50 sct := testdataSCT()
51 time.Sleep(time.Duration(500*reqNum)*time.Millisecond + ms.fixedDelay[logURL[0]])
52 return sct, nil
53 }
54
55 func evaluateSCTs(t *testing.T, got []*AssignedSCT, trail map[string]int) {
56 t.Helper()
57 for _, sct := range got {
58 if _, ok := trail[ctpolicy.BaseName]; ok {
59 trail[ctpolicy.BaseName]--
60 if trail[sct.LogURL[0:1]] > 0 {
61 trail[sct.LogURL[0:1]]--
62 }
63 } else {
64 trail[sct.LogURL[0:1]]--
65 }
66 }
67 for groupName, count := range trail {
68
69
70 if count > 0 {
71 for _, s := range got {
72 t.Errorf("%v\n", s.LogURL)
73 }
74 t.Errorf("Got %v. Received %d less SCTs from group %q than expected", got, count, groupName)
75 } else if count < 0 {
76 for _, s := range got {
77 t.Errorf("%v\n", s.LogURL)
78 }
79 t.Errorf("Got %v. Received %d more SCTs from group %q than expected", got, -count, groupName)
80 }
81 }
82 }
83
84 func TestGetSCTs(t *testing.T) {
85 testCases := []struct {
86 name string
87 sbMock Submitter
88 groups ctpolicy.LogPolicyData
89 resultTrail map[string]int
90 errRegexp *regexp.Regexp
91 }{
92 {
93 name: "singleGroupOneSCT",
94 sbMock: &mockSubmitter{fixedDelay: map[byte]time.Duration{'a': 0}, firstLetterURLReqNumber: make(map[byte]int)},
95 groups: ctpolicy.LogPolicyData{
96 "a": {
97 Name: "a",
98 LogURLs: map[string]bool{"a1.com": true, "a2.com": true},
99 MinInclusions: 1,
100 IsBase: false,
101 LogWeights: map[string]float32{"a1.com": 1.0, "a2.com": 1.0},
102 },
103 },
104 resultTrail: map[string]int{"a": 1},
105 },
106 {
107 name: "singleGroupMultiSCT",
108 sbMock: &mockSubmitter{fixedDelay: map[byte]time.Duration{'a': 0}, firstLetterURLReqNumber: make(map[byte]int)},
109 groups: ctpolicy.LogPolicyData{
110 "a": {
111 Name: "a",
112 LogURLs: map[string]bool{"a1.com": true, "a2.com": true, "a3.com": true, "a4.com": true, "a5.com": true},
113 MinInclusions: 3,
114 IsBase: false,
115 LogWeights: map[string]float32{"a1.com": 1.0, "a2.com": 1.0, "a3.com": 1.0, "a4.com": 1.0, "a5.com": 1.0},
116 },
117 },
118 resultTrail: map[string]int{"a": 3},
119 },
120 {
121 name: "chromeLike",
122 sbMock: &mockSubmitter{fixedDelay: map[byte]time.Duration{'a': 0, 'b': 2 * time.Second}, firstLetterURLReqNumber: make(map[byte]int)},
123 groups: ctpolicy.LogPolicyData{
124 "a": {
125 Name: "a",
126 LogURLs: map[string]bool{"a1.com": true, "a2.com": true, "a3.com": true, "a4.com": true},
127 MinInclusions: 1,
128 IsBase: false,
129 LogWeights: map[string]float32{"a1.com": 1.0, "a2.com": 1.0, "a3.com": 1.0, "a4.com": 1.0},
130 },
131 "b": {
132 Name: "b",
133 LogURLs: map[string]bool{"b1.com": true, "b2.com": true, "b3.com": true, "b4.com": true},
134 MinInclusions: 1,
135 IsBase: false,
136 LogWeights: map[string]float32{"b1.com": 1.0, "b2.com": 1.0, "b3.com": 1.0, "b4.com": 1.0},
137 },
138 ctpolicy.BaseName: {
139 Name: ctpolicy.BaseName,
140 LogURLs: map[string]bool{"a1.com": true, "a2.com": true, "a3.com": true, "a4.com": true, "b1.com": true, "b2.com": true, "b3.com": true, "b4.com": true},
141 MinInclusions: 5,
142 IsBase: true,
143 LogWeights: map[string]float32{"a1.com": 1.0, "a2.com": 1.0, "a3.com": 1.0, "a4.com": 1.0, "b1.com": 1.0, "b2.com": 1.0, "b3.com": 1.0, "b4.com": 1.0},
144 },
145 },
146 resultTrail: map[string]int{"a": 1, "b": 1, ctpolicy.BaseName: 5},
147 },
148 }
149
150 for _, tc := range testCases {
151 t.Run(tc.name, func(t *testing.T) {
152 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
153 defer cancel()
154 res, err := GetSCTs(ctx, tc.sbMock, []ct.ASN1Cert{{Data: []byte{0}}}, true, tc.groups)
155 if tc.resultTrail != nil {
156 evaluateSCTs(t, res, tc.resultTrail)
157 }
158 if tc.errRegexp == nil {
159 if err != nil {
160 t.Fatalf("GetSCTs() got err=%q want nil", err)
161 }
162 return
163 }
164
165 if err == nil {
166 t.Fatalf("GetSCTs() got err=nil want err matching: %q", tc.errRegexp)
167 }
168 if !tc.errRegexp.MatchString(err.Error()) {
169 t.Errorf("GetSCTs() got err=%q want err matching: %q", err, tc.errRegexp)
170 }
171 })
172 }
173 }
174
View as plain text