1// +build go1.13
2
3/*
4 *
5 * Copyright 2020 gRPC authors.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 */
20
21package certprovider
22
23import (
24	"context"
25	"crypto/tls"
26	"crypto/x509"
27	"errors"
28	"fmt"
29	"io/ioutil"
30	"reflect"
31	"testing"
32	"time"
33
34	"google.golang.org/grpc/internal/grpctest"
35	"google.golang.org/grpc/internal/testutils"
36	"google.golang.org/grpc/testdata"
37)
38
39const (
40	fakeProvider1Name       = "fake-certificate-provider-1"
41	fakeProvider2Name       = "fake-certificate-provider-2"
42	fakeConfig              = "my fake config"
43	defaultTestTimeout      = 5 * time.Second
44	defaultTestShortTimeout = 10 * time.Millisecond
45)
46
47var fpb1, fpb2 *fakeProviderBuilder
48
49func init() {
50	fpb1 = &fakeProviderBuilder{
51		name:         fakeProvider1Name,
52		providerChan: testutils.NewChannel(),
53	}
54	fpb2 = &fakeProviderBuilder{
55		name:         fakeProvider2Name,
56		providerChan: testutils.NewChannel(),
57	}
58	Register(fpb1)
59	Register(fpb2)
60}
61
62type s struct {
63	grpctest.Tester
64}
65
66func Test(t *testing.T) {
67	grpctest.RunSubTests(t, s{})
68}
69
70// fakeProviderBuilder builds new instances of fakeProvider and interprets the
71// config provided to it as a string.
72type fakeProviderBuilder struct {
73	name         string
74	providerChan *testutils.Channel
75}
76
77func (b *fakeProviderBuilder) ParseConfig(config interface{}) (*BuildableConfig, error) {
78	s, ok := config.(string)
79	if !ok {
80		return nil, fmt.Errorf("providerBuilder %s received config of type %T, want string", b.name, config)
81	}
82	return NewBuildableConfig(b.name, []byte(s), func(BuildOptions) Provider {
83		fp := &fakeProvider{
84			Distributor: NewDistributor(),
85			config:      s,
86		}
87		b.providerChan.Send(fp)
88		return fp
89	}), nil
90}
91
92func (b *fakeProviderBuilder) Name() string {
93	return b.name
94}
95
96// fakeProvider is an implementation of the Provider interface which provides a
97// method for tests to invoke to push new key materials.
98type fakeProvider struct {
99	*Distributor
100	config string
101}
102
103func (p *fakeProvider) Start(BuildOptions) Provider {
104	// This is practically a no-op since this provider doesn't do any work which
105	// needs to be started at this point.
106	return p
107}
108
109// newKeyMaterial allows tests to push new key material to the fake provider
110// which will be made available to users of this provider.
111func (p *fakeProvider) newKeyMaterial(km *KeyMaterial, err error) {
112	p.Distributor.Set(km, err)
113}
114
115// Close helps implement the Provider interface.
116func (p *fakeProvider) Close() {
117	p.Distributor.Stop()
118}
119
120// loadKeyMaterials is a helper to read cert/key files from testdata and convert
121// them into a KeyMaterialReader struct.
122func loadKeyMaterials(t *testing.T, cert, key, ca string) *KeyMaterial {
123	t.Helper()
124
125	certs, err := tls.LoadX509KeyPair(testdata.Path(cert), testdata.Path(key))
126	if err != nil {
127		t.Fatalf("Failed to load keyPair: %v", err)
128	}
129
130	pemData, err := ioutil.ReadFile(testdata.Path(ca))
131	if err != nil {
132		t.Fatal(err)
133	}
134	roots := x509.NewCertPool()
135	roots.AppendCertsFromPEM(pemData)
136	return &KeyMaterial{Certs: []tls.Certificate{certs}, Roots: roots}
137}
138
139// kmReader wraps the KeyMaterial method exposed by Provider and Distributor
140// implementations. Defining the interface here makes it possible to use the
141// same helper from both provider and distributor tests.
142type kmReader interface {
143	KeyMaterial(context.Context) (*KeyMaterial, error)
144}
145
146// readAndVerifyKeyMaterial attempts to read key material from the given
147// provider and compares it against the expected key material.
148func readAndVerifyKeyMaterial(ctx context.Context, kmr kmReader, wantKM *KeyMaterial) error {
149	gotKM, err := kmr.KeyMaterial(ctx)
150	if err != nil {
151		return fmt.Errorf("KeyMaterial(ctx) failed: %w", err)
152	}
153	return compareKeyMaterial(gotKM, wantKM)
154}
155
156func compareKeyMaterial(got, want *KeyMaterial) error {
157	// TODO(easwars): Remove all references to reflect.DeepEqual and use
158	// cmp.Equal instead. Currently, the later panics because x509.Certificate
159	// type defines an Equal method, but does not check for nil. This has been
160	// fixed in
161	// https://github.com/golang/go/commit/89865f8ba64ccb27f439cce6daaa37c9aa38f351,
162	// but this is only available starting go1.14. So, once we remove support
163	// for go1.13, we can make the switch.
164	if !reflect.DeepEqual(got, want) {
165		return fmt.Errorf("provider.KeyMaterial() = %+v, want %+v", got, want)
166	}
167	return nil
168}
169
170func createProvider(t *testing.T, name, config string, opts BuildOptions) Provider {
171	t.Helper()
172	prov, err := GetProvider(name, config, opts)
173	if err != nil {
174		t.Fatalf("GetProvider(%s, %s, %v) failed: %v", name, config, opts, err)
175	}
176	return prov
177}
178
179// TestStoreSingleProvider creates a single provider through the store and calls
180// methods on them.
181func (s) TestStoreSingleProvider(t *testing.T) {
182	prov := createProvider(t, fakeProvider1Name, fakeConfig, BuildOptions{CertName: "default"})
183	defer prov.Close()
184
185	// Our fakeProviderBuilder pushes newly created providers on a channel. Grab
186	// the fake provider from that channel.
187	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
188	defer cancel()
189	p, err := fpb1.providerChan.Receive(ctx)
190	if err != nil {
191		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
192	}
193	fakeProv := p.(*fakeProvider)
194
195	// Attempt to read from key material from the Provider returned by the
196	// store. This will fail because we have not pushed any key material into
197	// our fake provider.
198	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
199	defer sCancel()
200	if err := readAndVerifyKeyMaterial(sCtx, prov, nil); !errors.Is(err, context.DeadlineExceeded) {
201		t.Fatal(err)
202	}
203
204	// Load key material from testdata directory, push it into out fakeProvider
205	// and attempt to read from the Provider returned by the store.
206	testKM1 := loadKeyMaterials(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")
207	fakeProv.newKeyMaterial(testKM1, nil)
208	if err := readAndVerifyKeyMaterial(ctx, prov, testKM1); err != nil {
209		t.Fatal(err)
210	}
211
212	// Push new key material and read from the Provider. This should returned
213	// updated key material.
214	testKM2 := loadKeyMaterials(t, "x509/server2_cert.pem", "x509/server2_key.pem", "x509/client_ca_cert.pem")
215	fakeProv.newKeyMaterial(testKM2, nil)
216	if err := readAndVerifyKeyMaterial(ctx, prov, testKM2); err != nil {
217		t.Fatal(err)
218	}
219}
220
221// TestStoreSingleProviderSameConfigDifferentOpts creates multiple providers of
222// same type, for same configs but different keyMaterial options through the
223// store (and expects the store's sharing mechanism to kick in) and calls
224// methods on them.
225func (s) TestStoreSingleProviderSameConfigDifferentOpts(t *testing.T) {
226	// Create three readers on the same fake provider. Two of these readers use
227	// certName `foo`, while the third one uses certName `bar`.
228	optsFoo := BuildOptions{CertName: "foo"}
229	provFoo1 := createProvider(t, fakeProvider1Name, fakeConfig, optsFoo)
230	provFoo2 := createProvider(t, fakeProvider1Name, fakeConfig, optsFoo)
231	defer func() {
232		provFoo1.Close()
233		provFoo2.Close()
234	}()
235
236	// Our fakeProviderBuilder pushes newly created providers on a channel.
237	// Grab the fake provider for optsFoo.
238	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
239	defer cancel()
240	p, err := fpb1.providerChan.Receive(ctx)
241	if err != nil {
242		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
243	}
244	fakeProvFoo := p.(*fakeProvider)
245
246	// Make sure only provider was created by the builder so far. The store
247	// should be able to share the providers.
248	sCtx, sCancel := context.WithTimeout(context.Background(), defaultTestShortTimeout)
249	defer sCancel()
250	if _, err := fpb1.providerChan.Receive(sCtx); !errors.Is(err, context.DeadlineExceeded) {
251		t.Fatalf("A second provider created when expected to be shared by the store")
252	}
253
254	optsBar := BuildOptions{CertName: "bar"}
255	provBar1 := createProvider(t, fakeProvider1Name, fakeConfig, optsBar)
256	defer provBar1.Close()
257
258	// Grab the fake provider for optsBar.
259	p, err = fpb1.providerChan.Receive(ctx)
260	if err != nil {
261		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
262	}
263	fakeProvBar := p.(*fakeProvider)
264
265	// Push key material for optsFoo, and make sure the foo providers return
266	// appropriate key material and the bar provider times out.
267	fooKM := loadKeyMaterials(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")
268	fakeProvFoo.newKeyMaterial(fooKM, nil)
269	if err := readAndVerifyKeyMaterial(ctx, provFoo1, fooKM); err != nil {
270		t.Fatal(err)
271	}
272	if err := readAndVerifyKeyMaterial(ctx, provFoo2, fooKM); err != nil {
273		t.Fatal(err)
274	}
275	sCtx, sCancel = context.WithTimeout(context.Background(), defaultTestShortTimeout)
276	defer sCancel()
277	if err := readAndVerifyKeyMaterial(sCtx, provBar1, nil); !errors.Is(err, context.DeadlineExceeded) {
278		t.Fatal(err)
279	}
280
281	// Push key material for optsBar, and make sure the bar provider returns
282	// appropriate key material.
283	barKM := loadKeyMaterials(t, "x509/server2_cert.pem", "x509/server2_key.pem", "x509/client_ca_cert.pem")
284	fakeProvBar.newKeyMaterial(barKM, nil)
285	if err := readAndVerifyKeyMaterial(ctx, provBar1, barKM); err != nil {
286		t.Fatal(err)
287	}
288
289	// Make sure the above push of new key material does not affect foo readers.
290	if err := readAndVerifyKeyMaterial(ctx, provFoo1, fooKM); err != nil {
291		t.Fatal(err)
292	}
293}
294
295// TestStoreSingleProviderDifferentConfigs creates multiple instances of the
296// same type of provider through the store with different configs. The store
297// would end up creating different provider instances for these and no sharing
298// would take place.
299func (s) TestStoreSingleProviderDifferentConfigs(t *testing.T) {
300	// Create two providers of the same type, but with different configs.
301	opts := BuildOptions{CertName: "foo"}
302	cfg1 := fakeConfig + "1111"
303	cfg2 := fakeConfig + "2222"
304
305	prov1 := createProvider(t, fakeProvider1Name, cfg1, opts)
306	defer prov1.Close()
307	// Our fakeProviderBuilder pushes newly created providers on a channel. Grab
308	// the fake provider from that channel.
309	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
310	defer cancel()
311	p1, err := fpb1.providerChan.Receive(ctx)
312	if err != nil {
313		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
314	}
315	fakeProv1 := p1.(*fakeProvider)
316
317	prov2 := createProvider(t, fakeProvider1Name, cfg2, opts)
318	defer prov2.Close()
319	// Grab the second provider from the channel.
320	p2, err := fpb1.providerChan.Receive(ctx)
321	if err != nil {
322		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
323	}
324	fakeProv2 := p2.(*fakeProvider)
325
326	// Push the same key material into both fake providers and verify that the
327	// providers returned by the store return the appropriate key material.
328	km1 := loadKeyMaterials(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")
329	fakeProv1.newKeyMaterial(km1, nil)
330	fakeProv2.newKeyMaterial(km1, nil)
331	if err := readAndVerifyKeyMaterial(ctx, prov1, km1); err != nil {
332		t.Fatal(err)
333	}
334	if err := readAndVerifyKeyMaterial(ctx, prov2, km1); err != nil {
335		t.Fatal(err)
336	}
337
338	// Push new key material into only one of the fake providers and and verify
339	// that the providers returned by the store return the appropriate key
340	// material.
341	km2 := loadKeyMaterials(t, "x509/server2_cert.pem", "x509/server2_key.pem", "x509/client_ca_cert.pem")
342	fakeProv2.newKeyMaterial(km2, nil)
343	if err := readAndVerifyKeyMaterial(ctx, prov1, km1); err != nil {
344		t.Fatal(err)
345	}
346	if err := readAndVerifyKeyMaterial(ctx, prov2, km2); err != nil {
347		t.Fatal(err)
348	}
349
350	// Close one of the providers and verify that the other one is not affected.
351	prov1.Close()
352	if err := readAndVerifyKeyMaterial(ctx, prov2, km2); err != nil {
353		t.Fatal(err)
354	}
355}
356
357// TestStoreMultipleProviders creates providers of different types and makes
358// sure closing of one does not affect the other.
359func (s) TestStoreMultipleProviders(t *testing.T) {
360	opts := BuildOptions{CertName: "foo"}
361	prov1 := createProvider(t, fakeProvider1Name, fakeConfig, opts)
362	defer prov1.Close()
363	// Our fakeProviderBuilder pushes newly created providers on a channel. Grab
364	// the fake provider from that channel.
365	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
366	defer cancel()
367	p1, err := fpb1.providerChan.Receive(ctx)
368	if err != nil {
369		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider1Name)
370	}
371	fakeProv1 := p1.(*fakeProvider)
372
373	prov2 := createProvider(t, fakeProvider2Name, fakeConfig, opts)
374	defer prov2.Close()
375	// Grab the second provider from the channel.
376	p2, err := fpb2.providerChan.Receive(ctx)
377	if err != nil {
378		t.Fatalf("Timeout when expecting certProvider %q to be created", fakeProvider2Name)
379	}
380	fakeProv2 := p2.(*fakeProvider)
381
382	// Push the key material into both providers and verify that the
383	// readers return the appropriate key material.
384	km1 := loadKeyMaterials(t, "x509/server1_cert.pem", "x509/server1_key.pem", "x509/client_ca_cert.pem")
385	fakeProv1.newKeyMaterial(km1, nil)
386	km2 := loadKeyMaterials(t, "x509/server2_cert.pem", "x509/server2_key.pem", "x509/client_ca_cert.pem")
387	fakeProv2.newKeyMaterial(km2, nil)
388	if err := readAndVerifyKeyMaterial(ctx, prov1, km1); err != nil {
389		t.Fatal(err)
390	}
391	if err := readAndVerifyKeyMaterial(ctx, prov2, km2); err != nil {
392		t.Fatal(err)
393	}
394
395	// Close one of the providers and verify that the other one is not affected.
396	prov1.Close()
397	if err := readAndVerifyKeyMaterial(ctx, prov2, km2); err != nil {
398		t.Fatal(err)
399	}
400}
401