local-server/vendor/github.com/google/certificate-transparency-go/client/multilog_test.go
Mikaël Cluseau 4d889632f6 vendor
2018-06-17 18:32:44 +11:00

482 lines
15 KiB
Go

// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client
import (
"context"
"encoding/pem"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/golang/protobuf/ptypes"
tspb "github.com/golang/protobuf/ptypes/timestamp"
ct "github.com/google/certificate-transparency-go"
"github.com/google/certificate-transparency-go/client/configpb"
"github.com/google/certificate-transparency-go/testdata"
"github.com/google/certificate-transparency-go/x509util"
)
func TestNewTemporalLogClient(t *testing.T) {
ts0, _ := ptypes.TimestampProto(time.Date(2010, 9, 19, 11, 00, 00, 00, time.UTC))
ts1, _ := ptypes.TimestampProto(time.Date(2011, 9, 19, 11, 00, 00, 00, time.UTC))
ts2, _ := ptypes.TimestampProto(time.Date(2012, 9, 19, 11, 00, 00, 00, time.UTC))
ts2_5, _ := ptypes.TimestampProto(time.Date(2013, 3, 19, 11, 00, 00, 00, time.UTC))
ts3, _ := ptypes.TimestampProto(time.Date(2013, 9, 19, 11, 00, 00, 00, time.UTC))
ts4, _ := ptypes.TimestampProto(time.Date(2014, 9, 19, 11, 00, 00, 00, time.UTC))
tests := []struct {
cfg configpb.TemporalLogConfig
wantErr string
}{
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: nil, NotAfterLimit: nil},
},
},
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
},
},
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: nil, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
},
},
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: nil, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
},
},
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
},
},
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "threeA", NotAfterStart: ts2_5, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
},
},
wantErr: "previous interval ended at",
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
},
},
wantErr: "previous interval ended at",
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: nil, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts1},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
},
},
wantErr: "inverted",
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: nil},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
},
},
wantErr: "no upper bound",
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "three", NotAfterStart: nil, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
},
},
wantErr: "has no lower bound",
},
{
wantErr: "empty",
},
{
cfg: configpb.TemporalLogConfig{Shard: []*configpb.LogShardConfig{}},
wantErr: "empty",
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: ts1, NotAfterLimit: ts1},
},
},
wantErr: "inverted",
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: ts2, NotAfterLimit: ts1},
},
},
wantErr: "inverted",
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: &tspb.Timestamp{Seconds: -1, Nanos: -1}, NotAfterLimit: ts2},
},
},
wantErr: "failed to parse",
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "one", NotAfterStart: ts1, NotAfterLimit: &tspb.Timestamp{Seconds: -1, Nanos: -1}},
},
},
wantErr: "failed to parse",
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{
Uri: "one",
NotAfterStart: nil,
NotAfterLimit: nil,
PublicKeyDer: []byte{0x01, 0x02},
},
},
},
wantErr: "invalid public key",
},
}
for _, test := range tests {
_, err := NewTemporalLogClient(test.cfg, nil)
if err != nil {
if test.wantErr == "" {
t.Errorf("NewTemporalLogClient(%+v)=nil,%v; want _,nil", test.cfg, err)
} else if !strings.Contains(err.Error(), test.wantErr) {
t.Errorf("NewTemporalLogClient(%+v)=nil,%v; want _,%q", test.cfg, err, test.wantErr)
}
continue
}
if test.wantErr != "" {
t.Errorf("NewTemporalLogClient(%+v)=_, nil; want _,%q", test.cfg, test.wantErr)
}
}
}
func TestIndexByDate(t *testing.T) {
time0 := time.Date(2010, 9, 19, 11, 00, 00, 00, time.UTC)
time1 := time.Date(2011, 9, 19, 11, 00, 00, 00, time.UTC)
time1_9 := time.Date(2012, 9, 19, 10, 59, 59, 00, time.UTC)
time2 := time.Date(2012, 9, 19, 11, 00, 00, 00, time.UTC)
time2_5 := time.Date(2013, 3, 19, 11, 00, 00, 00, time.UTC)
time3 := time.Date(2013, 9, 19, 11, 00, 00, 00, time.UTC)
time4 := time.Date(2014, 9, 19, 11, 00, 00, 00, time.UTC)
ts0, _ := ptypes.TimestampProto(time0)
ts1, _ := ptypes.TimestampProto(time1)
ts2, _ := ptypes.TimestampProto(time2)
ts3, _ := ptypes.TimestampProto(time3)
ts4, _ := ptypes.TimestampProto(time4)
allCfg := configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "zero", NotAfterStart: nil, NotAfterLimit: ts0},
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
{Uri: "five", NotAfterStart: ts4, NotAfterLimit: nil},
},
}
uptoCfg := configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "zero", NotAfterStart: nil, NotAfterLimit: ts0},
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
},
}
fromCfg :=
configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "zero", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "one", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "two", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "three", NotAfterStart: ts3, NotAfterLimit: ts4},
{Uri: "four", NotAfterStart: ts4, NotAfterLimit: nil},
},
}
boundedCfg :=
configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: "zero", NotAfterStart: ts0, NotAfterLimit: ts1},
{Uri: "one", NotAfterStart: ts1, NotAfterLimit: ts2},
{Uri: "two", NotAfterStart: ts2, NotAfterLimit: ts3},
{Uri: "three", NotAfterStart: ts3, NotAfterLimit: ts4},
},
}
tests := []struct {
cfg configpb.TemporalLogConfig
when time.Time
want int
wantErr bool
}{
{cfg: allCfg, when: time.Date(2000, 9, 19, 11, 00, 00, 00, time.UTC), want: 0},
{cfg: allCfg, when: time0, want: 1},
{cfg: allCfg, when: time1, want: 2},
{cfg: allCfg, when: time1_9, want: 2},
{cfg: allCfg, when: time2, want: 3},
{cfg: allCfg, when: time2_5, want: 3},
{cfg: allCfg, when: time3, want: 4},
{cfg: allCfg, when: time4, want: 5},
{cfg: allCfg, when: time.Date(2015, 9, 19, 11, 00, 00, 00, time.UTC), want: 5},
{cfg: uptoCfg, when: time.Date(2000, 9, 19, 11, 00, 00, 00, time.UTC), want: 0},
{cfg: uptoCfg, when: time0, want: 1},
{cfg: uptoCfg, when: time1, want: 2},
{cfg: uptoCfg, when: time2, want: 3},
{cfg: uptoCfg, when: time2_5, want: 3},
{cfg: uptoCfg, when: time3, want: 4},
{cfg: uptoCfg, when: time4, wantErr: true},
{cfg: uptoCfg, when: time.Date(2015, 9, 19, 11, 00, 00, 00, time.UTC), wantErr: true},
{cfg: fromCfg, when: time.Date(2000, 9, 19, 11, 00, 00, 00, time.UTC), wantErr: true},
{cfg: fromCfg, when: time0, want: 0},
{cfg: fromCfg, when: time1, want: 1},
{cfg: fromCfg, when: time2, want: 2},
{cfg: fromCfg, when: time2_5, want: 2},
{cfg: fromCfg, when: time3, want: 3},
{cfg: fromCfg, when: time4, want: 4},
{cfg: fromCfg, when: time.Date(2015, 9, 19, 11, 00, 00, 00, time.UTC), want: 4},
{cfg: boundedCfg, when: time.Date(2000, 9, 19, 11, 00, 00, 00, time.UTC), wantErr: true},
{cfg: boundedCfg, when: time0, want: 0},
{cfg: boundedCfg, when: time1, want: 1},
{cfg: boundedCfg, when: time2, want: 2},
{cfg: boundedCfg, when: time2_5, want: 2},
{cfg: boundedCfg, when: time3, want: 3},
{cfg: boundedCfg, when: time4, wantErr: true},
{cfg: boundedCfg, when: time.Date(2015, 9, 19, 11, 00, 00, 00, time.UTC), wantErr: true},
}
for _, test := range tests {
tlc, err := NewTemporalLogClient(test.cfg, nil)
if err != nil {
t.Errorf("NewTemporalLogClient(%+v)=nil, %v; want _,nil", test.cfg, err)
continue
}
got, err := tlc.IndexByDate(test.when)
if err != nil {
if !test.wantErr {
t.Errorf("NewTemporalLogClient(%+v).idxByDate()=%d,%v; want %d,nil", test.cfg, got, err, test.want)
}
continue
}
if test.wantErr {
t.Errorf("NewTemporalLogClient(%+v).idxByDate(%v)=%d, nil; want _, 'no log found'", test.cfg, test.when, got)
}
if got != test.want {
t.Errorf("NewTemporalLogClient(%+v).idxByDate(%v)=%d, nil; want %d, nil", test.cfg, test.when, got, test.want)
}
}
}
func TestTemporalAddChain(t *testing.T) {
hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/ct/v1/add-chain":
data, _ := sctToJSON(testdata.TestCertProof)
w.Write(data)
case "/ct/v1/add-pre-chain":
data, _ := sctToJSON(testdata.TestPreCertProof)
w.Write(data)
default:
t.Fatalf("Incorrect URL path: %s", r.URL.Path)
}
}))
defer hs.Close()
cert, err := x509util.CertificateFromPEM(testdata.TestCertPEM)
if err != nil {
t.Fatalf("Failed to parse certificate from PEM: %v", err)
}
certChain := []ct.ASN1Cert{{Data: cert.Raw}}
precert, err := x509util.CertificateFromPEM(testdata.TestPreCertPEM)
if err != nil {
t.Fatalf("Failed to parse pre-certificate from PEM: %v", err)
}
issuer, err := x509util.CertificateFromPEM(testdata.CACertPEM)
if err != nil {
t.Fatalf("Failed to parse issuer certificate from PEM: %v", err)
}
precertChain := []ct.ASN1Cert{{Data: precert.Raw}, {Data: issuer.Raw}}
// Both have Not After = Jun 1 00:00:00 2022 GMT
ts1, _ := ptypes.TimestampProto(time.Date(2022, 5, 19, 11, 00, 00, 00, time.UTC))
ts2, _ := ptypes.TimestampProto(time.Date(2022, 6, 19, 11, 00, 00, 00, time.UTC))
p, _ := pem.Decode([]byte(testdata.LogPublicKeyPEM))
if p == nil {
t.Fatalf("Failed to parse public key from PEM: %v", err)
}
tests := []struct {
cfg configpb.TemporalLogConfig
wantErr bool
}{
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: hs.URL, NotAfterStart: nil, NotAfterLimit: nil, PublicKeyDer: p.Bytes},
},
},
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: hs.URL, NotAfterStart: nil, NotAfterLimit: ts2, PublicKeyDer: p.Bytes},
},
},
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: hs.URL, NotAfterStart: ts1, NotAfterLimit: nil, PublicKeyDer: p.Bytes},
},
},
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: hs.URL, NotAfterStart: ts1, NotAfterLimit: ts2, PublicKeyDer: p.Bytes},
},
},
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: hs.URL, NotAfterStart: nil, NotAfterLimit: ts1, PublicKeyDer: p.Bytes},
},
},
wantErr: true,
},
{
cfg: configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{Uri: hs.URL, NotAfterStart: ts2, NotAfterLimit: nil, PublicKeyDer: p.Bytes},
},
},
wantErr: true,
},
}
ctx := context.Background()
for _, test := range tests {
tlc, err := NewTemporalLogClient(test.cfg, nil)
if err != nil {
t.Errorf("NewTemporalLogClient(%+v)=nil, %v; want _,nil", test.cfg, err)
continue
}
_, err = tlc.AddChain(ctx, certChain)
if err != nil {
if !test.wantErr {
t.Errorf("AddChain()=nil,%v; want sct,nil", err)
}
} else if test.wantErr {
t.Errorf("AddChain()=sct,nil; want nil,_")
}
_, err = tlc.AddPreChain(ctx, precertChain)
if err != nil {
if !test.wantErr {
t.Errorf("AddPreChain()=nil,%v; want sct,nil", err)
}
} else if test.wantErr {
t.Errorf("AddPreChain()=sct,nil; want nil,_")
}
}
}
func TestTemporalAddChainErrors(t *testing.T) {
hs := serveSCTAt(t, "/ct/v1/add-chain", testdata.TestCertProof)
defer hs.Close()
cfg := configpb.TemporalLogConfig{
Shard: []*configpb.LogShardConfig{
{
Uri: hs.URL,
NotAfterStart: nil,
NotAfterLimit: nil,
},
},
}
ctx := context.Background()
tlc, err := NewTemporalLogClient(cfg, nil)
if err != nil {
t.Fatalf("NewTemporalLogClient(%+v)=nil, %v; want _,nil", cfg, err)
}
_, err = tlc.AddChain(ctx, nil)
if err == nil {
t.Errorf("AddChain(nil)=sct,nil; want nil, 'missing chain'")
}
_, err = tlc.AddChain(ctx, []ct.ASN1Cert{{Data: []byte{0x01, 0x02}}})
if err == nil {
t.Errorf("AddChain(nil)=sct,nil; want nil, 'failed to parse'")
}
}