// Copyright 2016 Google LLC. 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 fixchain import ( "net/http" "sync" "testing" "github.com/google/certificate-transparency-go/x509" ) // NewFixer() test func TestNewFixer(t *testing.T) { chains := make(chan []*x509.Certificate) errors := make(chan *FixError) var expectedChains [][]string var expectedErrs []errorType for _, test := range handleChainTests { expectedChains = append(expectedChains, test.expectedChains...) expectedErrs = append(expectedErrs, test.expectedErrs...) } var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() testChains(t, 0, expectedChains, chains) }() go func() { defer wg.Done() testErrors(t, 0, expectedErrs, errors) }() f := NewFixer(10, chains, errors, &http.Client{Transport: &testRoundTripper{}}, false) for _, test := range handleChainTests { f.QueueChain(GetTestCertificateFromPEM(t, test.cert), extractTestChain(t, 0, test.chain), extractTestRoots(t, 0, test.roots)) } f.Wait() close(chains) close(errors) wg.Wait() } // Fixer.fixServer() test func TestFixServer(t *testing.T) { cache := &urlCache{cache: newLockedCache(), client: &http.Client{Transport: &testRoundTripper{}}} f := &Fixer{cache: cache} var wg sync.WaitGroup fixServerTests := handleChainTests // Pass chains to be fixed one at a time to fixServer and check the chain // and errors produced are correct. for i, fst := range fixServerTests { chains := make(chan []*x509.Certificate) errors := make(chan *FixError) f.toFix = make(chan *toFix) f.chains = chains f.errors = errors wg.Add(2) go func() { defer wg.Done() testChains(t, i, fst.expectedChains, chains) }() go func() { defer wg.Done() testErrors(t, i, fst.expectedErrs, errors) }() f.wg.Add(1) go f.fixServer() f.QueueChain(GetTestCertificateFromPEM(t, fst.cert), extractTestChain(t, i, fst.chain), extractTestRoots(t, i, fst.roots)) f.Wait() close(chains) close(errors) wg.Wait() } // Pass multiple chains to be fixed to fixServer and check the chain and // errors produced are correct. chains := make(chan []*x509.Certificate) errors := make(chan *FixError) f.toFix = make(chan *toFix) f.chains = chains f.errors = errors var expectedChains [][]string var expectedErrs []errorType for _, fst := range fixServerTests { expectedChains = append(expectedChains, fst.expectedChains...) expectedErrs = append(expectedErrs, fst.expectedErrs...) } i := len(fixServerTests) wg.Add(2) go func() { defer wg.Done() testChains(t, i, expectedChains, chains) }() go func() { defer wg.Done() testErrors(t, i, expectedErrs, errors) }() f.wg.Add(1) go f.fixServer() for _, fst := range fixServerTests { f.QueueChain(GetTestCertificateFromPEM(t, fst.cert), extractTestChain(t, i, fst.chain), extractTestRoots(t, i, fst.roots)) } f.Wait() close(chains) close(errors) wg.Wait() } func TestRemoveSuperChains(t *testing.T) { superChainsTests := []struct { chains [][]string expectedChains [][]string }{ { chains: [][]string{ {googleLeaf, thawteIntermediate}, {googleLeaf}, }, expectedChains: [][]string{ {"Google"}, }, }, { chains: [][]string{ {googleLeaf, verisignRoot}, {googleLeaf, thawteIntermediate}, {googleLeaf}, }, expectedChains: [][]string{ {"Google"}, }, }, { chains: [][]string{ {googleLeaf, thawteIntermediate, verisignRoot}, {googleLeaf, thawteIntermediate}, {googleLeaf}, }, expectedChains: [][]string{ {"Google"}, }, }, { chains: [][]string{ {googleLeaf, thawteIntermediate, verisignRoot}, {googleLeaf}, }, expectedChains: [][]string{ {"Google"}, }, }, { chains: [][]string{ {googleLeaf, thawteIntermediate, verisignRoot}, {googleLeaf, verisignRoot}, {googleLeaf, thawteIntermediate}, }, expectedChains: [][]string{ {"Google", "Thawte"}, {"Google", "VeriSign"}, }, }, { chains: [][]string{ {testLeaf, testIntermediate2}, {googleLeaf, thawteIntermediate, verisignRoot}, {testLeaf, testIntermediate2, testIntermediate1, testRoot}, {googleLeaf, verisignRoot}, {testLeaf, testIntermediate2, testIntermediate1}, {googleLeaf, thawteIntermediate}, {testLeaf, googleLeaf, thawteIntermediate, verisignRoot}, }, expectedChains: [][]string{ {"Google", "Thawte"}, {"Google", "VeriSign"}, {"Leaf", "Intermediate2"}, {"Leaf", "Google", "Thawte", "VeriSign"}, }, }, } for i, test := range superChainsTests { var chains [][]*x509.Certificate for _, chain := range test.chains { chains = append(chains, extractTestChain(t, i, chain)) } matchTestChainList(t, i, test.expectedChains, removeSuperChains(chains)) } } // Fixer.updateCounters() tests func TestUpdateCounters(t *testing.T) { counterTests := []struct { errors []errorType reconstructed uint32 notReconstructed uint32 fixed uint32 notFixed uint32 }{ {[]errorType{}, 1, 0, 0, 0}, {[]errorType{VerifyFailed}, 0, 1, 1, 0}, {[]errorType{VerifyFailed, FixFailed}, 0, 1, 0, 1}, {[]errorType{ParseFailure}, 1, 0, 0, 0}, {[]errorType{ParseFailure, VerifyFailed}, 0, 1, 1, 0}, {[]errorType{ParseFailure, VerifyFailed, FixFailed}, 0, 1, 0, 1}, } for i, test := range counterTests { f := &Fixer{} var ferrs []*FixError for _, err := range test.errors { ferrs = append(ferrs, &FixError{Type: err}) } f.updateCounters(nil, ferrs) if f.reconstructed != test.reconstructed { t.Errorf("#%d: Incorrect value for reconstructed, wanted %d, got %d", i, test.reconstructed, f.reconstructed) } if f.notReconstructed != test.notReconstructed { t.Errorf("#%d: Incorrect value for notReconstructed, wanted %d, got %d", i, test.notReconstructed, f.notReconstructed) } if f.fixed != test.fixed { t.Errorf("#%d: Incorrect value for fixed, wanted %d, got %d", i, test.fixed, f.fixed) } if f.notFixed != test.notFixed { t.Errorf("#%d: Incorrect value for notFixed, wanted %d, got %d", i, test.notFixed, f.notFixed) } } } // Fixer.QueueChain() tests type fixerQueueTest struct { cert string chain []string roots []string dchain []string } var fixerQueueTests = []fixerQueueTest{ { cert: googleLeaf, chain: []string{verisignRoot, thawteIntermediate}, roots: []string{verisignRoot}, dchain: []string{"VeriSign", "Thawte"}, }, { cert: googleLeaf, chain: []string{verisignRoot, verisignRoot, thawteIntermediate}, roots: []string{verisignRoot}, dchain: []string{"VeriSign", "Thawte"}, }, { cert: googleLeaf, roots: []string{verisignRoot}, dchain: []string{}, }, } func testFixerQueueChain(t *testing.T, i int, qt *fixerQueueTest, f *Fixer) { defer f.wg.Done() fix := <-f.toFix // Check the deduped chain matchTestChain(t, i, qt.dchain, fix.chain.certs) } func TestFixerQueueChain(t *testing.T) { ch := make(chan *toFix) defer close(ch) f := &Fixer{toFix: ch} for i, qt := range fixerQueueTests { f.wg.Add(1) go testFixerQueueChain(t, i, &qt, f) chain := extractTestChain(t, i, qt.chain) roots := extractTestRoots(t, i, qt.roots) f.QueueChain(GetTestCertificateFromPEM(t, qt.cert), chain, roots) f.wg.Wait() } }