...

Source file src/github.com/google/certificate-transparency-go/submission/races_test.go

Documentation: github.com/google/certificate-transparency-go/submission

     1  // Copyright 2018 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 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  // Each request within same Log-group gets additional sleep period.
    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  		// It's possible to get more SCTs for Log-group than minimally-required.
    69  		// If group completion happened in-between Log-request and response. Or in case of group-intersection.
    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  			// If we reach here then we expected an error.
   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