1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package ctfe
16
17 import (
18 "crypto"
19 "errors"
20 "fmt"
21 "os"
22 "time"
23
24 ct "github.com/google/certificate-transparency-go"
25 "github.com/google/certificate-transparency-go/trillian/ctfe/configpb"
26 "github.com/google/certificate-transparency-go/x509"
27 "google.golang.org/protobuf/encoding/prototext"
28 "google.golang.org/protobuf/proto"
29 "k8s.io/klog/v2"
30 )
31
32
33
34 type ValidatedLogConfig struct {
35 Config *configpb.LogConfig
36 PubKey crypto.PublicKey
37 PrivKey proto.Message
38 KeyUsages []x509.ExtKeyUsage
39 NotAfterStart *time.Time
40 NotAfterLimit *time.Time
41 FrozenSTH *ct.SignedTreeHead
42 }
43
44
45
46
47 func LogConfigFromFile(filename string) ([]*configpb.LogConfig, error) {
48 cfgBytes, err := os.ReadFile(filename)
49 if err != nil {
50 return nil, err
51 }
52
53 var cfg configpb.LogConfigSet
54 if txtErr := prototext.Unmarshal(cfgBytes, &cfg); txtErr != nil {
55 if binErr := proto.Unmarshal(cfgBytes, &cfg); binErr != nil {
56 return nil, fmt.Errorf("failed to parse LogConfigSet from %q as text protobuf (%v) or binary protobuf (%v)", filename, txtErr, binErr)
57 }
58 }
59
60 if len(cfg.Config) == 0 {
61 return nil, errors.New("empty log config found")
62 }
63 return cfg.Config, nil
64 }
65
66
67
68
69 func ToMultiLogConfig(cfg []*configpb.LogConfig, beSpec string) *configpb.LogMultiConfig {
70 defaultBackend := &configpb.LogBackend{Name: "default", BackendSpec: beSpec}
71 for _, c := range cfg {
72 c.LogBackendName = defaultBackend.Name
73 }
74 return &configpb.LogMultiConfig{
75 LogConfigs: &configpb.LogConfigSet{Config: cfg},
76 Backends: &configpb.LogBackendSet{Backend: []*configpb.LogBackend{defaultBackend}},
77 }
78 }
79
80
81
82
83 func MultiLogConfigFromFile(filename string) (*configpb.LogMultiConfig, error) {
84 cfgBytes, err := os.ReadFile(filename)
85 if err != nil {
86 return nil, err
87 }
88
89 var cfg configpb.LogMultiConfig
90 if txtErr := prototext.Unmarshal(cfgBytes, &cfg); txtErr != nil {
91 if binErr := proto.Unmarshal(cfgBytes, &cfg); binErr != nil {
92 return nil, fmt.Errorf("failed to parse LogMultiConfig from %q as text protobuf (%v) or binary protobuf (%v)", filename, txtErr, binErr)
93 }
94 }
95
96 if len(cfg.LogConfigs.GetConfig()) == 0 || len(cfg.Backends.GetBackend()) == 0 {
97 return nil, errors.New("config is missing backends and/or log configs")
98 }
99 return &cfg, nil
100 }
101
102
103
104
105
106
107
108
109
110
111 func ValidateLogConfig(cfg *configpb.LogConfig) (*ValidatedLogConfig, error) {
112 if cfg.LogId == 0 {
113 return nil, errors.New("empty log ID")
114 }
115
116 vCfg := ValidatedLogConfig{Config: cfg}
117
118
119 if pubKey := cfg.PublicKey; pubKey != nil {
120 var err error
121 if vCfg.PubKey, err = x509.ParsePKIXPublicKey(pubKey.Der); err != nil {
122 return nil, fmt.Errorf("x509.ParsePKIXPublicKey: %w", err)
123 }
124 } else if cfg.IsMirror {
125 return nil, errors.New("empty public key for mirror")
126 } else if cfg.FrozenSth != nil {
127 return nil, errors.New("empty public key for frozen STH")
128 }
129
130
131 if !cfg.IsMirror {
132 if cfg.PrivateKey == nil {
133 return nil, errors.New("empty private key")
134 }
135 privKey, err := cfg.PrivateKey.UnmarshalNew()
136 if err != nil {
137 return nil, fmt.Errorf("invalid private key: %v", err)
138 }
139 vCfg.PrivKey = privKey
140 } else if cfg.PrivateKey != nil {
141 return nil, errors.New("unnecessary private key for mirror")
142 }
143
144 if cfg.RejectExpired && cfg.RejectUnexpired {
145 return nil, errors.New("rejecting all certificates")
146 }
147
148
149 if len(cfg.ExtKeyUsages) > 0 {
150 for _, kuStr := range cfg.ExtKeyUsages {
151 if ku, ok := stringToKeyUsage[kuStr]; ok {
152
153
154 if ku == x509.ExtKeyUsageAny {
155 klog.Infof("%s: Found ExtKeyUsageAny, allowing all EKUs", cfg.Prefix)
156 vCfg.KeyUsages = nil
157 break
158 }
159 vCfg.KeyUsages = append(vCfg.KeyUsages, ku)
160 } else {
161 return nil, fmt.Errorf("unknown extended key usage: %s", kuStr)
162 }
163 }
164 }
165
166
167 start, limit := cfg.NotAfterStart, cfg.NotAfterLimit
168 if start != nil {
169 vCfg.NotAfterStart = &time.Time{}
170 if err := start.CheckValid(); err != nil {
171 return nil, fmt.Errorf("invalid start timestamp: %v", err)
172 }
173 *vCfg.NotAfterStart = start.AsTime()
174 }
175 if limit != nil {
176 vCfg.NotAfterLimit = &time.Time{}
177 if err := limit.CheckValid(); err != nil {
178 return nil, fmt.Errorf("invalid limit timestamp: %v", err)
179 }
180 *vCfg.NotAfterLimit = limit.AsTime()
181 }
182 if start != nil && limit != nil && (*vCfg.NotAfterLimit).Before(*vCfg.NotAfterStart) {
183 return nil, errors.New("limit before start")
184 }
185
186 switch {
187 case cfg.MaxMergeDelaySec < 0:
188 return nil, errors.New("negative maximum merge delay")
189 case cfg.ExpectedMergeDelaySec < 0:
190 return nil, errors.New("negative expected merge delay")
191 case cfg.ExpectedMergeDelaySec > cfg.MaxMergeDelaySec:
192 return nil, errors.New("expected merge delay exceeds MMD")
193 }
194
195 if sth := cfg.FrozenSth; sth != nil {
196 verifier, err := ct.NewSignatureVerifier(vCfg.PubKey)
197 if err != nil {
198 return nil, fmt.Errorf("failed to create signature verifier: %v", err)
199 }
200 if vCfg.FrozenSTH, err = (&ct.GetSTHResponse{
201 TreeSize: uint64(sth.TreeSize),
202 Timestamp: uint64(sth.Timestamp),
203 SHA256RootHash: sth.Sha256RootHash,
204 TreeHeadSignature: sth.TreeHeadSignature,
205 }).ToSignedTreeHead(); err != nil {
206 return nil, fmt.Errorf("invalid frozen STH: %v", err)
207 }
208 if err := verifier.VerifySTHSignature(*vCfg.FrozenSTH); err != nil {
209 return nil, fmt.Errorf("signature verification failed: %v", err)
210 }
211 }
212
213 return &vCfg, nil
214 }
215
216
217 type LogBackendMap = map[string]*configpb.LogBackend
218
219
220
221
222 func BuildLogBackendMap(lbs *configpb.LogBackendSet) (LogBackendMap, error) {
223 lbm := make(LogBackendMap)
224 specs := make(map[string]bool)
225 for _, be := range lbs.Backend {
226 if len(be.Name) == 0 {
227 return nil, fmt.Errorf("empty backend name: %v", be)
228 }
229 if len(be.BackendSpec) == 0 {
230 return nil, fmt.Errorf("empty backend spec: %v", be)
231 }
232 if _, ok := lbm[be.Name]; ok {
233 return nil, fmt.Errorf("duplicate backend name: %v", be)
234 }
235 if ok := specs[be.BackendSpec]; ok {
236 return nil, fmt.Errorf("duplicate backend spec: %v", be)
237 }
238 lbm[be.Name] = be
239 specs[be.BackendSpec] = true
240 }
241 return lbm, nil
242 }
243
244 func validateConfigs(cfg []*configpb.LogConfig) error {
245
246
247 logNameMap := make(map[string]bool)
248 for _, logCfg := range cfg {
249 if _, err := ValidateLogConfig(logCfg); err != nil {
250 return fmt.Errorf("log config: %v: %v", err, logCfg)
251 }
252 if len(logCfg.Prefix) == 0 {
253 return fmt.Errorf("log config: empty prefix: %v", logCfg)
254 }
255 if logNameMap[logCfg.Prefix] {
256 return fmt.Errorf("log config: duplicate prefix: %s: %v", logCfg.Prefix, logCfg)
257 }
258 logNameMap[logCfg.Prefix] = true
259 }
260
261 return nil
262 }
263
264
265
266
267
268
269
270
271 func ValidateLogConfigs(cfg []*configpb.LogConfig) error {
272 if err := validateConfigs(cfg); err != nil {
273 return err
274 }
275
276
277 treeIDs := make(map[int64]bool)
278 for _, logCfg := range cfg {
279 if treeIDs[logCfg.LogId] {
280 return fmt.Errorf("log config: dup tree id: %d for: %v", logCfg.LogId, logCfg)
281 }
282 treeIDs[logCfg.LogId] = true
283 }
284
285 return nil
286 }
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303 func ValidateLogMultiConfig(cfg *configpb.LogMultiConfig) (LogBackendMap, error) {
304 backendMap, err := BuildLogBackendMap(cfg.Backends)
305 if err != nil {
306 return nil, err
307 }
308
309 if err := validateConfigs(cfg.GetLogConfigs().GetConfig()); err != nil {
310 return nil, err
311 }
312
313
314 logIDMap := make(map[string]bool)
315 for _, logCfg := range cfg.LogConfigs.Config {
316 if _, ok := backendMap[logCfg.LogBackendName]; !ok {
317 return nil, fmt.Errorf("log config: references undefined backend: %s: %v", logCfg.LogBackendName, logCfg)
318 }
319 logIDKey := fmt.Sprintf("%s-%d", logCfg.LogBackendName, logCfg.LogId)
320 if ok := logIDMap[logIDKey]; ok {
321 return nil, fmt.Errorf("log config: dup tree id: %d for: %v", logCfg.LogId, logCfg)
322 }
323 logIDMap[logIDKey] = true
324 }
325
326 return backendMap, nil
327 }
328
329 var stringToKeyUsage = map[string]x509.ExtKeyUsage{
330 "Any": x509.ExtKeyUsageAny,
331 "ServerAuth": x509.ExtKeyUsageServerAuth,
332 "ClientAuth": x509.ExtKeyUsageClientAuth,
333 "CodeSigning": x509.ExtKeyUsageCodeSigning,
334 "EmailProtection": x509.ExtKeyUsageEmailProtection,
335 "IPSECEndSystem": x509.ExtKeyUsageIPSECEndSystem,
336 "IPSECTunnel": x509.ExtKeyUsageIPSECTunnel,
337 "IPSECUser": x509.ExtKeyUsageIPSECUser,
338 "TimeStamping": x509.ExtKeyUsageTimeStamping,
339 "OCSPSigning": x509.ExtKeyUsageOCSPSigning,
340 "MicrosoftServerGatedCrypto": x509.ExtKeyUsageMicrosoftServerGatedCrypto,
341 "NetscapeServerGatedCrypto": x509.ExtKeyUsageNetscapeServerGatedCrypto,
342 }
343
View as plain text