1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ctfe
16
17 import (
18 "context"
19 "errors"
20 "fmt"
21 "net/http/httptest"
22 "strings"
23 "testing"
24 "time"
25
26 ct "github.com/google/certificate-transparency-go"
27 "github.com/google/certificate-transparency-go/trillian/ctfe/configpb"
28 "github.com/google/trillian/crypto/keys"
29 "github.com/google/trillian/crypto/keys/pem"
30 "github.com/google/trillian/crypto/keyspb"
31 "github.com/google/trillian/monitoring"
32 "google.golang.org/protobuf/types/known/anypb"
33 "google.golang.org/protobuf/types/known/timestamppb"
34 )
35
36 func init() {
37 keys.RegisterHandler(&keyspb.PEMKeyFile{}, pem.FromProto)
38 }
39
40 func TestSetUpInstance(t *testing.T) {
41 ctx := context.Background()
42
43 privKey := mustMarshalAny(&keyspb.PEMKeyFile{Path: "../testdata/ct-http-server.privkey.pem", Password: "dirk"})
44 missingPrivKey := mustMarshalAny(&keyspb.PEMKeyFile{Path: "../testdata/bogus.privkey.pem", Password: "dirk"})
45 wrongPassPrivKey := mustMarshalAny(&keyspb.PEMKeyFile{Path: "../testdata/ct-http-server.privkey.pem", Password: "dirkly"})
46 pubKey := mustReadPublicKey("../testdata/ct-http-server.pubkey.pem")
47
48 var tests = []struct {
49 desc string
50 cfg *configpb.LogConfig
51 wantErr string
52 }{
53 {
54 desc: "valid",
55 cfg: &configpb.LogConfig{
56 LogId: 1,
57 Prefix: "log",
58 RootsPemFile: []string{"../testdata/fake-ca.cert"},
59 PrivateKey: privKey,
60 },
61 },
62 {
63 desc: "valid-mirror",
64 cfg: &configpb.LogConfig{
65 LogId: 1,
66 Prefix: "log",
67 RootsPemFile: []string{"../testdata/fake-ca.cert"},
68 PublicKey: pubKey,
69 IsMirror: true,
70 },
71 },
72 {
73 desc: "no-roots",
74 cfg: &configpb.LogConfig{
75 LogId: 1,
76 Prefix: "log",
77 PrivateKey: privKey,
78 },
79 wantErr: "specify RootsPemFile",
80 },
81 {
82 desc: "no-roots-mirror",
83 cfg: &configpb.LogConfig{
84 LogId: 1,
85 Prefix: "log",
86 PublicKey: pubKey,
87 IsMirror: true,
88 },
89 },
90 {
91 desc: "missing-root-cert",
92 cfg: &configpb.LogConfig{
93 LogId: 1,
94 Prefix: "log",
95 RootsPemFile: []string{"../testdata/bogus.cert"},
96 PrivateKey: privKey,
97 },
98 wantErr: "failed to read trusted roots",
99 },
100 {
101 desc: "missing-privkey",
102 cfg: &configpb.LogConfig{
103 LogId: 1,
104 Prefix: "log",
105 RootsPemFile: []string{"../testdata/fake-ca.cert"},
106 PrivateKey: missingPrivKey,
107 },
108 wantErr: "failed to load private key",
109 },
110 {
111 desc: "privkey-wrong-password",
112 cfg: &configpb.LogConfig{
113 LogId: 1,
114 Prefix: "log",
115 RootsPemFile: []string{"../testdata/fake-ca.cert"},
116 PrivateKey: wrongPassPrivKey,
117 },
118 wantErr: "failed to load private key",
119 },
120 {
121 desc: "valid-ekus-1",
122 cfg: &configpb.LogConfig{
123 LogId: 1,
124 Prefix: "log",
125 RootsPemFile: []string{"../testdata/fake-ca.cert"},
126 PrivateKey: privKey,
127 ExtKeyUsages: []string{"Any"},
128 },
129 },
130 {
131 desc: "valid-ekus-2",
132 cfg: &configpb.LogConfig{
133 LogId: 1,
134 Prefix: "log",
135 RootsPemFile: []string{"../testdata/fake-ca.cert"},
136 PrivateKey: privKey,
137 ExtKeyUsages: []string{"Any", "ServerAuth", "TimeStamping"},
138 },
139 },
140 {
141 desc: "valid-reject-ext",
142 cfg: &configpb.LogConfig{
143 LogId: 1,
144 Prefix: "log",
145 RootsPemFile: []string{"../testdata/fake-ca.cert"},
146 PrivateKey: privKey,
147 RejectExtensions: []string{"1.2.3.4", "5.6.7.8"},
148 },
149 },
150 {
151 desc: "invalid-reject-ext",
152 cfg: &configpb.LogConfig{
153 LogId: 1,
154 Prefix: "log",
155 RootsPemFile: []string{"../testdata/fake-ca.cert"},
156 PrivateKey: privKey,
157 RejectExtensions: []string{"1.2.3.4", "one.banana.two.bananas"},
158 },
159 wantErr: "one",
160 },
161 }
162
163 for _, test := range tests {
164 t.Run(test.desc, func(t *testing.T) {
165 vCfg, err := ValidateLogConfig(test.cfg)
166 if err != nil {
167 t.Fatalf("ValidateLogConfig(): %v", err)
168 }
169 opts := InstanceOptions{Validated: vCfg, Deadline: time.Second, MetricFactory: monitoring.InertMetricFactory{}}
170
171 if _, err := SetUpInstance(ctx, opts); err != nil {
172 if test.wantErr == "" {
173 t.Errorf("SetUpInstance()=_,%v; want _,nil", err)
174 } else if !strings.Contains(err.Error(), test.wantErr) {
175 t.Errorf("SetUpInstance()=_,%v; want err containing %q", err, test.wantErr)
176 }
177 return
178 }
179 if test.wantErr != "" {
180 t.Errorf("SetUpInstance()=_,nil; want err containing %q", test.wantErr)
181 }
182 })
183 }
184 }
185
186 func equivalentTimes(a *time.Time, b *timestamppb.Timestamp) bool {
187 if a == nil && b == nil {
188 return true
189 }
190 if a == nil {
191
192 return false
193 }
194 tsA := timestamppb.New(*a)
195 return tsA.AsTime().Format(time.RFC3339Nano) == b.AsTime().Format(time.RFC3339Nano)
196 }
197
198 func TestSetUpInstanceSetsValidationOpts(t *testing.T) {
199 ctx := context.Background()
200
201 start := timestamppb.New(time.Unix(10000, 0))
202 limit := timestamppb.New(time.Unix(12000, 0))
203
204 privKey, err := anypb.New(&keyspb.PEMKeyFile{Path: "../testdata/ct-http-server.privkey.pem", Password: "dirk"})
205 if err != nil {
206 t.Fatalf("Could not marshal private key proto: %v", err)
207 }
208 var tests = []struct {
209 desc string
210 cfg *configpb.LogConfig
211 }{
212 {
213 desc: "no validation opts",
214 cfg: &configpb.LogConfig{
215 LogId: 1,
216 Prefix: "/log",
217 RootsPemFile: []string{"../testdata/fake-ca.cert"},
218 PrivateKey: privKey,
219 },
220 },
221 {
222 desc: "notAfterStart only",
223 cfg: &configpb.LogConfig{
224 LogId: 1,
225 Prefix: "/log",
226 RootsPemFile: []string{"../testdata/fake-ca.cert"},
227 PrivateKey: privKey,
228 NotAfterStart: start,
229 },
230 },
231 {
232 desc: "notAfter range",
233 cfg: &configpb.LogConfig{
234 LogId: 1,
235 Prefix: "/log",
236 RootsPemFile: []string{"../testdata/fake-ca.cert"},
237 PrivateKey: privKey,
238 NotAfterStart: start,
239 NotAfterLimit: limit,
240 },
241 },
242 {
243 desc: "caOnly",
244 cfg: &configpb.LogConfig{
245 LogId: 1,
246 Prefix: "/log",
247 RootsPemFile: []string{"../testdata/fake-ca.cert"},
248 PrivateKey: privKey,
249 AcceptOnlyCa: true,
250 },
251 },
252 }
253
254 for _, test := range tests {
255 t.Run(test.desc, func(t *testing.T) {
256 vCfg, err := ValidateLogConfig(test.cfg)
257 if err != nil {
258 t.Fatalf("ValidateLogConfig(): %v", err)
259 }
260 opts := InstanceOptions{Validated: vCfg, Deadline: time.Second, MetricFactory: monitoring.InertMetricFactory{}}
261
262 inst, err := SetUpInstance(ctx, opts)
263 if err != nil {
264 t.Fatalf("%v: SetUpInstance() = %v, want no error", test.desc, err)
265 }
266 addChainHandler, ok := inst.Handlers[test.cfg.Prefix+ct.AddChainPath]
267 if !ok {
268 t.Fatal("Couldn't find AddChain handler")
269 }
270 gotOpts := addChainHandler.Info.validationOpts
271 if got, want := gotOpts.notAfterStart, test.cfg.NotAfterStart; want != nil && !equivalentTimes(got, want) {
272 t.Errorf("%v: handler notAfterStart %v, want %v", test.desc, got, want)
273 }
274 if got, want := gotOpts.notAfterLimit, test.cfg.NotAfterLimit; want != nil && !equivalentTimes(got, want) {
275 t.Errorf("%v: handler notAfterLimit %v, want %v", test.desc, got, want)
276 }
277 if got, want := gotOpts.acceptOnlyCA, test.cfg.AcceptOnlyCa; got != want {
278 t.Errorf("%v: handler acceptOnlyCA %v, want %v", test.desc, got, want)
279 }
280 })
281 }
282 }
283
284 func TestErrorMasking(t *testing.T) {
285 info := logInfo{}
286 w := httptest.NewRecorder()
287 prefix := "Internal Server Error"
288 err := errors.New("well that's bad")
289 info.SendHTTPError(w, 500, err)
290 if got, want := w.Body.String(), fmt.Sprintf("%s\n%v\n", prefix, err); got != want {
291 t.Errorf("SendHTTPError: got %s, want %s", got, want)
292 }
293 info.instanceOpts.MaskInternalErrors = true
294 w = httptest.NewRecorder()
295 info.SendHTTPError(w, 500, err)
296 if got, want := w.Body.String(), prefix+"\n"; got != want {
297 t.Errorf("SendHTTPError: got %s, want %s", got, want)
298 }
299
300 }
301
View as plain text