...

Source file src/github.com/google/certificate-transparency-go/trillian/ctfe/sth_test.go

Documentation: github.com/google/certificate-transparency-go/trillian/ctfe

     1  // Copyright 2019 Google LLC. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ctfe
    16  
    17  import (
    18  	"context"
    19  	"crypto"
    20  	"errors"
    21  	"io"
    22  	"reflect"
    23  	"strings"
    24  	"testing"
    25  
    26  	"github.com/golang/mock/gomock"
    27  	ct "github.com/google/certificate-transparency-go"
    28  	"github.com/google/certificate-transparency-go/tls"
    29  	"github.com/google/certificate-transparency-go/trillian/mockclient"
    30  	"github.com/google/trillian"
    31  	"github.com/google/trillian/types"
    32  )
    33  
    34  type testCase struct {
    35  	desc     string
    36  	ctxSetup func(ctx context.Context) context.Context
    37  	ms       MirrorSTHStorage // Only set for mirror getter tests.
    38  	slr      *trillian.GetLatestSignedLogRootResponse
    39  	slrErr   error
    40  	sig      []byte // Only set (and sigErr) for log getter tests.
    41  	sigErr   error
    42  	wantSTH  *ct.SignedTreeHead
    43  	errStr   string
    44  }
    45  
    46  // commonTests are valid for both cases, mostly basic parameter checks
    47  // and type / error handling.
    48  func commonTests(t *testing.T) []testCase {
    49  	t.Helper()
    50  	return []testCase{
    51  		{
    52  			desc: "bad quota value",
    53  			ctxSetup: func(ctx context.Context) context.Context {
    54  				return context.WithValue(ctx, remoteQuotaCtxKey, []byte("not a string value"))
    55  			},
    56  			errStr: "incorrect quota",
    57  		},
    58  		{
    59  			desc:   "latest root RPC fails",
    60  			slrErr: errors.New("slr failed"),
    61  			errStr: "slr failed",
    62  		},
    63  		{
    64  			desc:   "nil slr",
    65  			slr:    &trillian.GetLatestSignedLogRootResponse{},
    66  			errStr: "no log root",
    67  		},
    68  		{
    69  			desc:   "bad slr",
    70  			slr:    &trillian.GetLatestSignedLogRootResponse{SignedLogRoot: &trillian.SignedLogRoot{LogRoot: []byte("not tls encoded")}},
    71  			errStr: "unmarshal root: log_root:\"not tls",
    72  		},
    73  		{
    74  			desc: "bad hash",
    75  			slr: &trillian.GetLatestSignedLogRootResponse{
    76  				SignedLogRoot: mustMarshalRoot(t,
    77  					&types.LogRootV1{RootHash: []byte("not a 32 byte hash")}),
    78  			},
    79  			errStr: "bad hash size",
    80  		},
    81  	}
    82  }
    83  
    84  // logTests apply only to the LogSTHGetter where things are signed.
    85  func logTests(t *testing.T) []testCase {
    86  	t.Helper()
    87  	return []testCase{
    88  		{
    89  			desc: "signer error",
    90  			slr: &trillian.GetLatestSignedLogRootResponse{
    91  				SignedLogRoot: mustMarshalRoot(t,
    92  					&types.LogRootV1{RootHash: []byte("12345678123456781234567812345678")}),
    93  			},
    94  			sigErr: errors.New("not signing that"),
    95  			errStr: "sign tree head: not signing",
    96  		},
    97  		{
    98  			desc: "empty sig",
    99  			slr: &trillian.GetLatestSignedLogRootResponse{
   100  				SignedLogRoot: mustMarshalRoot(t,
   101  					&types.LogRootV1{RootHash: []byte("12345678123456781234567812345678")}),
   102  			},
   103  			sig:    []byte{},
   104  			errStr: "sign tree head: <nil>",
   105  		},
   106  		{
   107  			desc: "ok",
   108  			slr: &trillian.GetLatestSignedLogRootResponse{
   109  				SignedLogRoot: mustMarshalRoot(t,
   110  					&types.LogRootV1{
   111  						// Ensure response contains all fields needed for the CT STH.
   112  						TreeSize:       12345,
   113  						TimestampNanos: 987654321,
   114  						RootHash:       []byte("12345678123456781234567812345678")}),
   115  			},
   116  			sig: []byte("signedit"),
   117  			wantSTH: &ct.SignedTreeHead{
   118  				Timestamp:      987,
   119  				SHA256RootHash: hashFromString("12345678123456781234567812345678"),
   120  				TreeHeadSignature: ct.DigitallySigned{
   121  					Algorithm: tls.SignatureAndHashAlgorithm{
   122  						Hash:      tls.SHA256,
   123  						Signature: tls.SignatureAlgorithmFromPubKey(tls.Anonymous),
   124  					},
   125  					Signature: []byte("signedit"),
   126  				},
   127  				TreeSize: 12345,
   128  			},
   129  		},
   130  	}
   131  }
   132  
   133  // mirrorTests apply only to the MirrorSTHGetter where sth is read from a store.
   134  func mirrorTests(t *testing.T) []testCase {
   135  	t.Helper()
   136  	return []testCase{
   137  		{
   138  			desc: "bad mirror storage",
   139  			ms: &fakeMirrorSTHStorage{
   140  				err: errors.New("mirror store failed"),
   141  			},
   142  			slr: &trillian.GetLatestSignedLogRootResponse{
   143  				SignedLogRoot: mustMarshalRoot(t,
   144  					&types.LogRootV1{
   145  						// Ensure response contains all fields needed for the CT STH.
   146  						TreeSize:       12345,
   147  						TimestampNanos: 987654321,
   148  						RootHash:       []byte("12345678123456781234567812345678")}),
   149  			},
   150  			errStr: "mirror store failed",
   151  		},
   152  		{
   153  			desc: "ok",
   154  			ms: &fakeMirrorSTHStorage{
   155  				sth: &ct.SignedTreeHead{
   156  					Timestamp:      987,
   157  					SHA256RootHash: hashFromString("12345678123456781234567812345678"),
   158  					TreeHeadSignature: ct.DigitallySigned{
   159  						Algorithm: tls.SignatureAndHashAlgorithm{
   160  							Hash:      tls.SHA256,
   161  							Signature: tls.SignatureAlgorithmFromPubKey(tls.Anonymous),
   162  						},
   163  						Signature: []byte("signedit"),
   164  					},
   165  					TreeSize: 12345,
   166  				},
   167  			},
   168  			slr: &trillian.GetLatestSignedLogRootResponse{
   169  				SignedLogRoot: mustMarshalRoot(t,
   170  					&types.LogRootV1{
   171  						// Ensure response contains all fields needed for the CT STH.
   172  						TreeSize:       12345,
   173  						TimestampNanos: 987654321,
   174  						RootHash:       []byte("12345678123456781234567812345678")}),
   175  			},
   176  			wantSTH: &ct.SignedTreeHead{
   177  				Timestamp:      987,
   178  				SHA256RootHash: hashFromString("12345678123456781234567812345678"),
   179  				TreeHeadSignature: ct.DigitallySigned{
   180  					Algorithm: tls.SignatureAndHashAlgorithm{
   181  						Hash:      tls.SHA256,
   182  						Signature: tls.SignatureAlgorithmFromPubKey(tls.Anonymous),
   183  					},
   184  					Signature: []byte("signedit"),
   185  				},
   186  				TreeSize: 12345,
   187  			},
   188  		},
   189  	}
   190  }
   191  
   192  func TestLogSTHGetter(t *testing.T) {
   193  	// Note: Does not test signature cache interaction as this is inside
   194  	// signV1TreeHead and covered by other tests.
   195  	tests := make([]testCase, 0, 30)
   196  	tests = append(tests, commonTests(t)...)
   197  	tests = append(tests, logTests(t)...)
   198  
   199  	for _, tc := range tests {
   200  		t.Run(tc.desc, func(t *testing.T) {
   201  			ctrl := gomock.NewController(t)
   202  			rpcCl := mockclient.NewMockTrillianLogClient(ctrl)
   203  			if tc.slr != nil || tc.slrErr != nil {
   204  				rpcCl.EXPECT().GetLatestSignedLogRoot(gomock.Any(), cmpMatcher{&trillian.GetLatestSignedLogRootRequest{LogId: 99}}).Return(tc.slr, tc.slrErr)
   205  			}
   206  
   207  			sthg := LogSTHGetter{li: &logInfo{rpcClient: rpcCl, logID: 99, signer: &fakeSigner{sig: tc.sig, err: tc.sigErr}}}
   208  			ctx := context.Background()
   209  			if tc.ctxSetup != nil {
   210  				ctx = tc.ctxSetup(ctx)
   211  			}
   212  
   213  			sth, err := sthg.GetSTH(ctx)
   214  			if len(tc.errStr) > 0 {
   215  				if err == nil || !strings.Contains(err.Error(), tc.errStr) {
   216  					t.Errorf("GetSTH()=%v, %v want: nil, err containing %s", sth, err, tc.errStr)
   217  				}
   218  			} else {
   219  				if err != nil || !reflect.DeepEqual(sth, tc.wantSTH) {
   220  					t.Errorf("GetSTH()=%v, %v, want: %v, nil", sth, err, tc.wantSTH)
   221  				}
   222  			}
   223  			ctrl.Finish()
   224  		})
   225  	}
   226  }
   227  
   228  func TestMirrorSTHGetter(t *testing.T) {
   229  	// Note: This does not test the operation of MirrorSTHStorage. Implementations
   230  	// of this need their own tests.
   231  	tests := make([]testCase, 0, 30)
   232  	tests = append(tests, commonTests(t)...)
   233  	tests = append(tests, mirrorTests(t)...)
   234  
   235  	for _, tc := range tests {
   236  		t.Run(tc.desc, func(t *testing.T) {
   237  			ctrl := gomock.NewController(t)
   238  			rpcCl := mockclient.NewMockTrillianLogClient(ctrl)
   239  			if tc.slr != nil || tc.slrErr != nil {
   240  				rpcCl.EXPECT().GetLatestSignedLogRoot(gomock.Any(), cmpMatcher{&trillian.GetLatestSignedLogRootRequest{LogId: 99}}).Return(tc.slr, tc.slrErr)
   241  			}
   242  
   243  			sthg := MirrorSTHGetter{li: &logInfo{rpcClient: rpcCl, logID: 99}, st: tc.ms}
   244  			ctx := context.Background()
   245  			if tc.ctxSetup != nil {
   246  				ctx = tc.ctxSetup(ctx)
   247  			}
   248  
   249  			sth, err := sthg.GetSTH(ctx)
   250  			if len(tc.errStr) > 0 {
   251  				if err == nil || !strings.Contains(err.Error(), tc.errStr) {
   252  					t.Errorf("GetSTH()=%v, %v want: nil, err containing %s", sth, err, tc.errStr)
   253  				}
   254  			} else {
   255  				if err != nil || !reflect.DeepEqual(sth, tc.wantSTH) {
   256  					t.Errorf("GetSTH()=%v, %v, want: %v, nil", sth, err, tc.wantSTH)
   257  				}
   258  			}
   259  			ctrl.Finish()
   260  		})
   261  	}
   262  }
   263  
   264  func TestFrozenSTHGetter(t *testing.T) {
   265  	sth := &ct.SignedTreeHead{TreeSize: 123, Version: 1}
   266  	f := FrozenSTHGetter{sth: sth}
   267  	// This should always return its canned value and never an error.
   268  	if sth2, err := f.GetSTH(context.Background()); sth2 != sth || err != nil {
   269  		t.Fatalf("FrozenSTHGetter.GetSTH()=%v, %v, want: %v, nil", sth2, err, sth)
   270  	}
   271  }
   272  
   273  func TestDefaultMirrorSTHStorage(t *testing.T) {
   274  	s, err := DefaultMirrorSTHFactory{}.NewStorage([32]byte{})
   275  	if err != nil {
   276  		t.Fatalf("NewStorage()=%v, %v, want: no err", s, err)
   277  	}
   278  	// We expect a "not implemented" error from this and nil sth.
   279  	sth, err := s.GetMirrorSTH(context.Background(), 9999)
   280  	if sth != nil || err == nil || !strings.Contains(err.Error(), "not impl") {
   281  		t.Fatalf("MirrorSTHStorage.GetMirrorSTH(): got: %v, %v, want: nil, err containing 'not impl'", sth, err)
   282  	}
   283  }
   284  
   285  type fakeSigner struct {
   286  	sig []byte
   287  	err error
   288  }
   289  
   290  func (f *fakeSigner) Public() crypto.PublicKey {
   291  	return []byte("this key is public") // This will map to tls.Anonymous.
   292  }
   293  
   294  func (f *fakeSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
   295  	return f.sig, f.err
   296  }
   297  
   298  type fakeMirrorSTHStorage struct {
   299  	sth *ct.SignedTreeHead
   300  	err error
   301  }
   302  
   303  func (f *fakeMirrorSTHStorage) GetMirrorSTH(ctx context.Context, maxTreeSize int64) (*ct.SignedTreeHead, error) {
   304  	return f.sth, f.err
   305  }
   306  
   307  func hashFromString(str string) [32]byte {
   308  	var hash = [32]byte{}
   309  	copy(hash[:], []byte(str))
   310  	return hash
   311  }
   312  

View as plain text