From 978cb4403cd78dfc62247462849323b8a4888133 Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Thu, 29 Jun 2023 18:35:12 +0000 Subject: [PATCH 01/62] rename certificateListExt to CRL --- security/advancedtls/crl.go | 24 ++++++++++++------------ security/advancedtls/crl_test.go | 10 +++++----- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index bb490a5c8dba..19603408fff5 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -83,9 +83,9 @@ func (s RevocationStatus) String() string { return [...]string{"RevocationUndetermined", "RevocationUnrevoked", "RevocationRevoked"}[s] } -// certificateListExt contains a pkix.CertificateList and parsed +// CRL contains a pkix.CertificateList and parsed // extensions that aren't provided by the golang CRL parser. -type certificateListExt struct { +type CRL struct { CertList *x509.RevocationList // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. AuthorityKeyID []byte @@ -215,12 +215,12 @@ func checkChain(chain []*x509.Certificate, cfg RevocationConfig) RevocationStatu return chainStatus } -func cachedCrl(rawIssuer []byte, cache Cache) (*certificateListExt, bool) { +func cachedCrl(rawIssuer []byte, cache Cache) (*CRL, bool) { val, ok := cache.Get(hex.EncodeToString(rawIssuer)) if !ok { return nil, false } - crl, ok := val.(*certificateListExt) + crl, ok := val.(*CRL) if !ok { return nil, false } @@ -232,7 +232,7 @@ func cachedCrl(rawIssuer []byte, cache Cache) (*certificateListExt, bool) { } // fetchIssuerCRL fetches and verifies the CRL for rawIssuer from disk or cache if configured in cfg. -func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*certificateListExt, error) { +func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*CRL, error) { if cfg.Cache != nil { if crl, ok := cachedCrl(rawIssuer, cfg.Cache); ok { return crl, nil @@ -277,7 +277,7 @@ func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg Revoca return revocation } -func checkCertRevocation(c *x509.Certificate, crl *certificateListExt) (RevocationStatus, error) { +func checkCertRevocation(c *x509.Certificate, crl *CRL) (RevocationStatus, error) { // Per section 5.3.3 we prime the certificate issuer with the CRL issuer. // Subsequent entries use the previous entry's issuer. rawEntryIssuer := crl.RawIssuer @@ -375,11 +375,11 @@ type issuingDistributionPoint struct { // parseCRLExtensions parses the extensions for a CRL // and checks that they're supported by the parser. -func parseCRLExtensions(c *x509.RevocationList) (*certificateListExt, error) { +func parseCRLExtensions(c *x509.RevocationList) (*CRL, error) { if c == nil { return nil, errors.New("c is nil, expected any value") } - certList := &certificateListExt{CertList: c} + certList := &CRL{CertList: c} for _, ext := range c.Extensions { switch { @@ -424,8 +424,8 @@ func parseCRLExtensions(c *x509.RevocationList) (*certificateListExt, error) { return certList, nil } -func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*certificateListExt, error) { - var parsedCRL *certificateListExt +func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { + var parsedCRL *CRL // 6.3.3 (a) (1) (ii) // According to X509_LOOKUP_hash_dir the format is issuer_hash.rN where N is an increasing number. // There are no gaps, so we break when we can't find a file. @@ -449,7 +449,7 @@ func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*certificateListExt, erro // Parsing errors for a CRL shouldn't happen so fail. return nil, fmt.Errorf("parseRevocationList(%v) failed: %v", crlPath, err) } - var certList *certificateListExt + var certList *CRL if certList, err = parseCRLExtensions(crl); err != nil { grpclogLogger.Infof("fetchCRL: unsupported crl %v: %v", crlPath, err) // Continue to find a supported CRL @@ -475,7 +475,7 @@ func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*certificateListExt, erro return parsedCRL, nil } -func verifyCRL(crl *certificateListExt, rawIssuer []byte, chain []*x509.Certificate) error { +func verifyCRL(crl *CRL, rawIssuer []byte, chain []*x509.Certificate) error { // RFC5280, 6.3.3 (f) Obtain and validateate the certification path for the issuer of the complete CRL // We intentionally limit our CRLs to be signed with the same certificate path as the certificate // so we can use the chain from the connection. diff --git a/security/advancedtls/crl_test.go b/security/advancedtls/crl_test.go index 021f10d35ae5..99e6dd764ab7 100644 --- a/security/advancedtls/crl_test.go +++ b/security/advancedtls/crl_test.go @@ -223,7 +223,7 @@ qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 if err != nil { t.Fatalf("parseRevocationList(dummyCrlFile) failed: %v", err) } - crlExt := &certificateListExt{CertList: crl} + crlExt := &CRL{CertList: crl} var crlIssuer pkix.Name = crl.Issuer var revocationTests = []struct { @@ -338,7 +338,7 @@ func makeChain(t *testing.T, name string) []*x509.Certificate { return certChain } -func loadCRL(t *testing.T, path string) *certificateListExt { +func loadCRL(t *testing.T, path string) *CRL { b, err := os.ReadFile(path) if err != nil { t.Fatalf("readFile(%v) failed err = %v", path, err) @@ -371,7 +371,7 @@ func TestCachedCRL(t *testing.T) { }{ { desc: "Valid", - val: &certificateListExt{ + val: &CRL{ CertList: &x509.RevocationList{ NextUpdate: time.Now().Add(time.Hour), }}, @@ -379,7 +379,7 @@ func TestCachedCRL(t *testing.T) { }, { desc: "Expired", - val: &certificateListExt{ + val: &CRL{ CertList: &x509.RevocationList{ NextUpdate: time.Now().Add(-time.Hour), }}, @@ -459,7 +459,7 @@ func TestVerifyCrl(t *testing.T) { verifyTests := []struct { desc string - crl *certificateListExt + crl *CRL certs []*x509.Certificate cert *x509.Certificate errWant string From 7d032e04a6911a78e39ffe8c6ca84c90becd84f8 Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Thu, 29 Jun 2023 18:35:24 +0000 Subject: [PATCH 02/62] CRLProvider file --- security/advancedtls/crl_provider.go | 32 ++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 security/advancedtls/crl_provider.go diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go new file mode 100644 index 000000000000..9023317a22b9 --- /dev/null +++ b/security/advancedtls/crl_provider.go @@ -0,0 +1,32 @@ +/* + * + * Copyright 2023 gRPC authors. + * + * 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 advancedtls + +import ( + "context" + "crypto/x509" +) + +type CRLProvider interface { + // Callers are expected to use the returned value as read-only. + CRL(ctx context.Context, cert x509.Certificate) (*CRL, error) + + // Close cleans up resources allocated by the Provider. + Close() +} From cdbc298853f0cbe01227fa70e581b774a013122e Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Thu, 29 Jun 2023 18:39:19 +0000 Subject: [PATCH 03/62] Add CRLProvider to RevocationConfig --- security/advancedtls/crl.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index 19603408fff5..2129d0cfd505 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -65,6 +65,11 @@ type RevocationConfig struct { AllowUndetermined bool // Cache will store CRL files if not nil, otherwise files are reloaded for every lookup. Cache Cache + // CRLProvider is an alternative to using RootDir directly for the + // X509_LOOKUP_hash_dir approach to CRL files. If set, the CRLProvider's CRL + // function will be called when looking up and fetching CRLs during the + // handshake. + CRLProvider CRLProvider } // RevocationStatus is the revocation status for a certificate or chain. From 95991d8682bc751f8684f9f4409287b219e72f45 Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Thu, 29 Jun 2023 18:49:14 +0000 Subject: [PATCH 04/62] Beginning refactor of CRL handling --- security/advancedtls/crl.go | 16 +++++++++++--- security/advancedtls/crl_deprecated.go | 24 ++++++++++----------- security/advancedtls/crl_deprecated_test.go | 10 ++++----- security/advancedtls/crl_test.go | 2 +- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index 2129d0cfd505..9eb0cbf077e7 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -244,7 +244,7 @@ func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg Revo } } - crl, err := fetchCRL(rawIssuer, cfg) + crl, err := fetchCRLOpenSSLHashDir(rawIssuer, cfg) if err != nil { return nil, fmt.Errorf("fetchCRL() failed: %v", err) } @@ -258,13 +258,23 @@ func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg Revo return crl, nil } +func fetchCRL(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*CRL, error) { + if cfg.CRLProvider != nil { + //TODO + } else { + return fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) + } + // TODO + return nil, nil +} + // checkCert checks a single certificate against the CRL defined in the certificate. // It will fetch and verify the CRL(s) defined in the root directory specified by cfg. // If we can't load any authoritative CRL files, the status is RevocationUndetermined. // c is the certificate to check. // crlVerifyCrt is the group of possible certificates to verify the crl. func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) RevocationStatus { - crl, err := fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) + crl, err := fetchCRL(c, crlVerifyCrt, cfg) if err != nil { // We couldn't load any CRL files for the certificate, so we don't know if it's RevocationUnrevoked or not. grpclogLogger.Warningf("getIssuerCRL(%v) err = %v", c.Issuer, err) @@ -429,7 +439,7 @@ func parseCRLExtensions(c *x509.RevocationList) (*CRL, error) { return certList, nil } -func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { +func fetchCRLOpenSSLHashDir(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { var parsedCRL *CRL // 6.3.3 (a) (1) (ii) // According to X509_LOOKUP_hash_dir the format is issuer_hash.rN where N is an increasing number. diff --git a/security/advancedtls/crl_deprecated.go b/security/advancedtls/crl_deprecated.go index a54a2f6e55c5..cd7b3d1228ad 100644 --- a/security/advancedtls/crl_deprecated.go +++ b/security/advancedtls/crl_deprecated.go @@ -83,9 +83,9 @@ func (s RevocationStatus) String() string { return [...]string{"RevocationUndetermined", "RevocationUnrevoked", "RevocationRevoked"}[s] } -// certificateListExt contains a pkix.CertificateList and parsed +// CRL contains a pkix.CertificateList and parsed // extensions that aren't provided by the golang CRL parser. -type certificateListExt struct { +type CRL struct { CertList *pkix.CertificateList // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. AuthorityKeyID []byte @@ -215,12 +215,12 @@ func checkChain(chain []*x509.Certificate, cfg RevocationConfig) RevocationStatu return chainStatus } -func cachedCrl(rawIssuer []byte, cache Cache) (*certificateListExt, bool) { +func cachedCrl(rawIssuer []byte, cache Cache) (*CRL, bool) { val, ok := cache.Get(hex.EncodeToString(rawIssuer)) if !ok { return nil, false } - crl, ok := val.(*certificateListExt) + crl, ok := val.(*CRL) if !ok { return nil, false } @@ -232,7 +232,7 @@ func cachedCrl(rawIssuer []byte, cache Cache) (*certificateListExt, bool) { } // fetchIssuerCRL fetches and verifies the CRL for rawIssuer from disk or cache if configured in cfg. -func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*certificateListExt, error) { +func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*CRL, error) { if cfg.Cache != nil { if crl, ok := cachedCrl(rawIssuer, cfg.Cache); ok { return crl, nil @@ -277,7 +277,7 @@ func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg Revoca return revocation } -func checkCertRevocation(c *x509.Certificate, crl *certificateListExt) (RevocationStatus, error) { +func checkCertRevocation(c *x509.Certificate, crl *CRL) (RevocationStatus, error) { // Per section 5.3.3 we prime the certificate issuer with the CRL issuer. // Subsequent entries use the previous entry's issuer. rawEntryIssuer := crl.RawIssuer @@ -375,11 +375,11 @@ type issuingDistributionPoint struct { // parseCRLExtensions parses the extensions for a CRL // and checks that they're supported by the parser. -func parseCRLExtensions(c *pkix.CertificateList) (*certificateListExt, error) { +func parseCRLExtensions(c *pkix.CertificateList) (*CRL, error) { if c == nil { return nil, errors.New("c is nil, expected any value") } - certList := &certificateListExt{CertList: c} + certList := &CRL{CertList: c} for _, ext := range c.TBSCertList.Extensions { switch { @@ -424,8 +424,8 @@ func parseCRLExtensions(c *pkix.CertificateList) (*certificateListExt, error) { return certList, nil } -func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*certificateListExt, error) { - var parsedCRL *certificateListExt +func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { + var parsedCRL *CRL // 6.3.3 (a) (1) (ii) // According to X509_LOOKUP_hash_dir the format is issuer_hash.rN where N is an increasing number. // There are no gaps, so we break when we can't find a file. @@ -449,7 +449,7 @@ func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*certificateListExt, erro // Parsing errors for a CRL shouldn't happen so fail. return nil, fmt.Errorf("x509.ParseCrl(%v) failed: %v", crlPath, err) } - var certList *certificateListExt + var certList *CRL if certList, err = parseCRLExtensions(crl); err != nil { grpclogLogger.Infof("fetchCRL: unsupported crl %v: %v", crlPath, err) // Continue to find a supported CRL @@ -475,7 +475,7 @@ func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*certificateListExt, erro return parsedCRL, nil } -func verifyCRL(crl *certificateListExt, rawIssuer []byte, chain []*x509.Certificate) error { +func verifyCRL(crl *CRL, rawIssuer []byte, chain []*x509.Certificate) error { // RFC5280, 6.3.3 (f) Obtain and validateate the certification path for the issuer of the complete CRL // We intentionally limit our CRLs to be signed with the same certificate path as the certificate // so we can use the chain from the connection. diff --git a/security/advancedtls/crl_deprecated_test.go b/security/advancedtls/crl_deprecated_test.go index f51ab95d00ce..e5ee05c62cc7 100644 --- a/security/advancedtls/crl_deprecated_test.go +++ b/security/advancedtls/crl_deprecated_test.go @@ -223,7 +223,7 @@ qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 if err != nil { t.Fatalf("x509.ParseCRL(dummyCrlFile) failed: %v", err) } - crlExt := &certificateListExt{CertList: crl} + crlExt := &CRL{CertList: crl} var crlIssuer pkix.Name crlIssuer.FillFromRDNSequence(&crl.TBSCertList.Issuer) @@ -339,7 +339,7 @@ func makeChain(t *testing.T, name string) []*x509.Certificate { return certChain } -func loadCRL(t *testing.T, path string) *certificateListExt { +func loadCRL(t *testing.T, path string) *CRL { b, err := os.ReadFile(path) if err != nil { t.Fatalf("readFile(%v) failed err = %v", path, err) @@ -372,7 +372,7 @@ func TestCachedCRL(t *testing.T) { }{ { desc: "Valid", - val: &certificateListExt{ + val: &CRL{ CertList: &pkix.CertificateList{ TBSCertList: pkix.TBSCertificateList{ NextUpdate: time.Now().Add(time.Hour), @@ -382,7 +382,7 @@ func TestCachedCRL(t *testing.T) { }, { desc: "Expired", - val: &certificateListExt{ + val: &CRL{ CertList: &pkix.CertificateList{ TBSCertList: pkix.TBSCertificateList{ NextUpdate: time.Now().Add(-time.Hour), @@ -464,7 +464,7 @@ func TestVerifyCrl(t *testing.T) { verifyTests := []struct { desc string - crl *certificateListExt + crl *CRL certs []*x509.Certificate cert *x509.Certificate errWant string diff --git a/security/advancedtls/crl_test.go b/security/advancedtls/crl_test.go index 99e6dd764ab7..dd2f83061403 100644 --- a/security/advancedtls/crl_test.go +++ b/security/advancedtls/crl_test.go @@ -729,7 +729,7 @@ func TestIssuerNonPrintableString(t *testing.T) { if err != nil { t.Fatalf("failed to decode issuer: %s", err) } - _, err = fetchCRL(rawIssuer, RevocationConfig{RootDir: testdata.Path("crl")}) + _, err = fetchCRLOpenSSLHashDir(rawIssuer, RevocationConfig{RootDir: testdata.Path("crl")}) if err != nil { t.Fatalf("fetchCRL failed: %s", err) } From 32e31588f95b23b461922c4b89885fbe91930c3b Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Thu, 29 Jun 2023 19:59:26 +0000 Subject: [PATCH 05/62] Shell of StaticCRLProvider --- security/advancedtls/crl.go | 8 +++++--- security/advancedtls/crl_provider.go | 17 +++++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index 9eb0cbf077e7..d825ef10dda9 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -260,12 +260,14 @@ func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg Revo func fetchCRL(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*CRL, error) { if cfg.CRLProvider != nil { - //TODO + crl, err := cfg.CRLProvider.CRL(c) + if err != nil { + return nil, fmt.Errorf("CrlProvider failed err = %v", err) + } + return crl, nil } else { return fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) } - // TODO - return nil, nil } // checkCert checks a single certificate against the CRL defined in the certificate. diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 9023317a22b9..276db374c178 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -19,14 +19,23 @@ package advancedtls import ( - "context" "crypto/x509" ) type CRLProvider interface { // Callers are expected to use the returned value as read-only. - CRL(ctx context.Context, cert x509.Certificate) (*CRL, error) + CRL(cert *x509.Certificate) (*CRL, error) +} + +type StaticCRLProvider struct { + crls map[string]*CRL +} + +func (p *StaticCRLProvider) AddCRL(crl *CRL) { + p.crls[crl.CertList.Issuer.ToRDNSequence().String()] = crl +} - // Close cleans up resources allocated by the Provider. - Close() +func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { + // TODO what to do if no CRL found + return p.crls[cert.Issuer.ToRDNSequence().String()], nil } From 00de36e6aaca70f4f2b88c221dbff5c899fb1053 Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Thu, 29 Jun 2023 20:22:07 +0000 Subject: [PATCH 06/62] basic static crl provider test --- security/advancedtls/crl.go | 35 ++++++++++++++++- security/advancedtls/crl_provider.go | 11 +++++- security/advancedtls/crl_provider_test.go | 46 +++++++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 security/advancedtls/crl_provider_test.go diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index d825ef10dda9..7d044413f475 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -279,7 +279,7 @@ func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg Revoca crl, err := fetchCRL(c, crlVerifyCrt, cfg) if err != nil { // We couldn't load any CRL files for the certificate, so we don't know if it's RevocationUnrevoked or not. - grpclogLogger.Warningf("getIssuerCRL(%v) err = %v", c.Issuer, err) + grpclogLogger.Warningf("fetchCRL(%v) err = %v", c.Issuer, err) return RevocationUndetermined } revocation, err := checkCertRevocation(c, crl) @@ -441,6 +441,39 @@ func parseCRLExtensions(c *x509.RevocationList) (*CRL, error) { return certList, nil } +func readCRLFile(crlPath string) (*CRL, error) { + crlBytes, err := os.ReadFile(crlPath) + if err != nil { + return nil, err + } + + crl, err := parseRevocationList(crlBytes) + if err != nil { + return nil, fmt.Errorf("parseRevocationList(%v) failed: %v", crlPath, err) + } + var certList *CRL + if certList, err = parseCRLExtensions(crl); err != nil { + grpclogLogger.Infof("fetchCRL: unsupported crl %v: %v", crlPath, err) + return nil, fmt.Errorf("fetchCRL: unsupported crl %v: %v", crlPath, err) + } + + rawCRLIssuer, err := extractCRLIssuer(crlBytes) + if err != nil { + return nil, err + } + certList.RawIssuer = rawCRLIssuer + // // RFC5280, 6.3.3 (b) Verify the issuer and scope of the complete CRL. + // TODO we may need to do this check elsewhere + // HOWEVER, the structure of CRL providers is that we let people use their + // own impl, so they should be making sure issuers match if + // bytes.Equal(rawIssuer, rawCRLIssuer) { + // parsedCRL = certList + // // Continue to find the highest number in the .rN suffix. + // continue + // } + return certList, nil +} + func fetchCRLOpenSSLHashDir(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { var parsedCRL *CRL // 6.3.3 (a) (1) (ii) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 276db374c178..709726472dd4 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -28,14 +28,23 @@ type CRLProvider interface { } type StaticCRLProvider struct { + // TODO CRL is sort of our internal representation - provide an API for + // people to read into it, or provide a simpler type in the API then + // internally convert to this form crls map[string]*CRL } +func MakeStaticCRLProvider() *StaticCRLProvider { + p := StaticCRLProvider{} + p.crls = make(map[string]*CRL) + return &p +} + func (p *StaticCRLProvider) AddCRL(crl *CRL) { p.crls[crl.CertList.Issuer.ToRDNSequence().String()] = crl } func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { - // TODO what to do if no CRL found + // TODO handle no CRL found return p.crls[cert.Issuer.ToRDNSequence().String()], nil } diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go new file mode 100644 index 000000000000..4bc79bce108a --- /dev/null +++ b/security/advancedtls/crl_provider_test.go @@ -0,0 +1,46 @@ +/* + * + * Copyright 2023 gRPC authors. + * + * 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 advancedtls + +import ( + "fmt" + "testing" + + "google.golang.org/grpc/security/advancedtls/testdata" +) + +func TestStaticCRLProvider(t *testing.T) { + p := MakeStaticCRLProvider() + for i := 1; i <= 6; i++ { + // TODO use loadCRL in test instead of new readCRLFile func + crl, err := readCRLFile(testdata.Path(fmt.Sprintf("crl/%d.crl", i))) + if err != nil { + t.Fatalf("TODO couldn't read file") + } + p.AddCRL(crl) + } + certs := makeChain(t, testdata.Path("crl/unrevoked.pem")) + crl, err := p.CRL(certs[0]) + if err != nil { + t.Fatalf("TODO fetching from provider") + } + if crl == nil { + t.Fatalf("TODO CRL is nil") + } +} From 8033cab7bfff577f3a750e5d1bee97746d0315f7 Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Fri, 30 Jun 2023 17:26:39 +0000 Subject: [PATCH 07/62] use loadCRL helper --- security/advancedtls/crl.go | 33 ----------------------- security/advancedtls/crl_provider_test.go | 6 +---- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index 7d044413f475..c435c8191b3d 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -441,39 +441,6 @@ func parseCRLExtensions(c *x509.RevocationList) (*CRL, error) { return certList, nil } -func readCRLFile(crlPath string) (*CRL, error) { - crlBytes, err := os.ReadFile(crlPath) - if err != nil { - return nil, err - } - - crl, err := parseRevocationList(crlBytes) - if err != nil { - return nil, fmt.Errorf("parseRevocationList(%v) failed: %v", crlPath, err) - } - var certList *CRL - if certList, err = parseCRLExtensions(crl); err != nil { - grpclogLogger.Infof("fetchCRL: unsupported crl %v: %v", crlPath, err) - return nil, fmt.Errorf("fetchCRL: unsupported crl %v: %v", crlPath, err) - } - - rawCRLIssuer, err := extractCRLIssuer(crlBytes) - if err != nil { - return nil, err - } - certList.RawIssuer = rawCRLIssuer - // // RFC5280, 6.3.3 (b) Verify the issuer and scope of the complete CRL. - // TODO we may need to do this check elsewhere - // HOWEVER, the structure of CRL providers is that we let people use their - // own impl, so they should be making sure issuers match if - // bytes.Equal(rawIssuer, rawCRLIssuer) { - // parsedCRL = certList - // // Continue to find the highest number in the .rN suffix. - // continue - // } - return certList, nil -} - func fetchCRLOpenSSLHashDir(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { var parsedCRL *CRL // 6.3.3 (a) (1) (ii) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 4bc79bce108a..d7d662e46b92 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -28,11 +28,7 @@ import ( func TestStaticCRLProvider(t *testing.T) { p := MakeStaticCRLProvider() for i := 1; i <= 6; i++ { - // TODO use loadCRL in test instead of new readCRLFile func - crl, err := readCRLFile(testdata.Path(fmt.Sprintf("crl/%d.crl", i))) - if err != nil { - t.Fatalf("TODO couldn't read file") - } + crl := loadCRL(t, testdata.Path(fmt.Sprintf("crl/%d.crl", i))) p.AddCRL(crl) } certs := makeChain(t, testdata.Path("crl/unrevoked.pem")) From 338a7f47e0e84845cac913864a317d891a9f993e Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Fri, 30 Jun 2023 17:38:51 +0000 Subject: [PATCH 08/62] refactor of CRL loading --- security/advancedtls/crl.go | 30 ++++++++++++++++++++++++++++++ security/advancedtls/crl_test.go | 18 +++--------------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index c435c8191b3d..f26525cb5595 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -90,6 +90,8 @@ func (s RevocationStatus) String() string { // CRL contains a pkix.CertificateList and parsed // extensions that aren't provided by the golang CRL parser. +// GRPC requires certain specifics for CRLs - all CRLs should be loaded using +// NewCRL() for bytes directly or ReadCRLFile() to read directly from a filepath type CRL struct { CertList *x509.RevocationList // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. @@ -97,6 +99,34 @@ type CRL struct { RawIssuer []byte } +func NewCRL(b []byte) (*CRL, error) { + crl, err := parseRevocationList(b) + if err != nil { + return nil, fmt.Errorf("parseCrl() failed err = %v", err) + } + crlExt, err := parseCRLExtensions(crl) + if err != nil { + return nil, fmt.Errorf("parseCRLExtensions() failed err = %v", err) + } + crlExt.RawIssuer, err = extractCRLIssuer(b) + if err != nil { + return nil, fmt.Errorf("extractCRLIssuer() failed err= %v", err) + } + return crlExt, nil +} + +func ReadCRLFile(path string) (*CRL, error) { + b, err := os.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("readFile(%v) failed err = %v", path, err) + } + crl, err := NewCRL(b) + if err != nil { + return nil, fmt.Errorf("ReadCRLFile(%v) failed err = %v", path, err) + } + return crl, nil +} + const tagDirectoryName = 4 var ( diff --git a/security/advancedtls/crl_test.go b/security/advancedtls/crl_test.go index dd2f83061403..a674d4051be9 100644 --- a/security/advancedtls/crl_test.go +++ b/security/advancedtls/crl_test.go @@ -339,23 +339,11 @@ func makeChain(t *testing.T, name string) []*x509.Certificate { } func loadCRL(t *testing.T, path string) *CRL { - b, err := os.ReadFile(path) + crl, err := ReadCRLFile(path) if err != nil { - t.Fatalf("readFile(%v) failed err = %v", path, err) + t.Fatalf("ReadCRLFile(%v) failed err = %v", path, err) } - crl, err := parseRevocationList(b) - if err != nil { - t.Fatalf("parseCrl(%v) failed err = %v", path, err) - } - crlExt, err := parseCRLExtensions(crl) - if err != nil { - t.Fatalf("parseCRLExtensions(%v) failed err = %v", path, err) - } - crlExt.RawIssuer, err = extractCRLIssuer(b) - if err != nil { - t.Fatalf("extractCRLIssuer(%v) failed err= %v", path, err) - } - return crlExt + return crl } func TestCachedCRL(t *testing.T) { From d1f63fe97ecdaed0ec26a7531d39e79146dc360c Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Fri, 30 Jun 2023 17:41:39 +0000 Subject: [PATCH 09/62] Table tests --- security/advancedtls/crl_provider_test.go | 27 ++++++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index d7d662e46b92..a334f7e4c47b 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -19,6 +19,7 @@ package advancedtls import ( + "crypto/x509" "fmt" "testing" @@ -31,12 +32,26 @@ func TestStaticCRLProvider(t *testing.T) { crl := loadCRL(t, testdata.Path(fmt.Sprintf("crl/%d.crl", i))) p.AddCRL(crl) } - certs := makeChain(t, testdata.Path("crl/unrevoked.pem")) - crl, err := p.CRL(certs[0]) - if err != nil { - t.Fatalf("TODO fetching from provider") + + tests := []struct { + desc string + certs []*x509.Certificate + }{ + { + desc: "TODO", + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + }, } - if crl == nil { - t.Fatalf("TODO CRL is nil") + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + crl, err := p.CRL(tt.certs[0]) + if err != nil { + t.Fatalf("TODO fetching from provider") + } + if crl == nil { + t.Fatalf("TODO CRL is nil") + } + }) } } From 01afa97b06bef76649964f6adda372d2782391ba Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Fri, 30 Jun 2023 17:47:15 +0000 Subject: [PATCH 10/62] Table tests --- security/advancedtls/crl_provider_test.go | 34 +++++++++++++++++------ 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index a334f7e4c47b..eacbb5a90271 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -34,23 +34,39 @@ func TestStaticCRLProvider(t *testing.T) { } tests := []struct { - desc string - certs []*x509.Certificate + desc string + certs []*x509.Certificate + expectNoCRL bool }{ { - desc: "TODO", + desc: "Unrevoked chain", certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), }, + { + desc: "Revoked Intermediate chain", + certs: makeChain(t, testdata.Path("crl/revokedInt.pem")), + }, + { + desc: "Revoked leaf chain", + certs: makeChain(t, testdata.Path("crl/revokedLeaf.pem")), + }, + { + desc: "Chain with no CRL for issuer", + certs: makeChain(t, testdata.Path("client_cert_1.pem")), + expectNoCRL: true, + }, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - crl, err := p.CRL(tt.certs[0]) - if err != nil { - t.Fatalf("TODO fetching from provider") - } - if crl == nil { - t.Fatalf("TODO CRL is nil") + for _, c := range tt.certs { + crl, err := p.CRL(c) + if err != nil { + t.Fatalf("Expected error fetch from provider: %v", err) + } + if crl == nil && !tt.expectNoCRL { + t.Fatalf("CRL is unexpectedly nil") + } } }) } From 401eb79584c2852dfe2ee7f7b85c0ab7917cb9de Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Fri, 30 Jun 2023 20:18:23 +0000 Subject: [PATCH 11/62] Add tests with Static CRL provider --- security/advancedtls/crl.go | 10 +++++++++- security/advancedtls/crl_test.go | 20 +++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index f26525cb5595..dcc8dae738f9 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -294,6 +294,10 @@ func fetchCRL(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg Revocat if err != nil { return nil, fmt.Errorf("CrlProvider failed err = %v", err) } + if crl == nil { + // TODO print out cert info here? What cert contents are okay to have in a log? + return nil, fmt.Errorf("no CRL found for certificate's issuer") + } return crl, nil } else { return fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) @@ -308,7 +312,11 @@ func fetchCRL(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg Revocat func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) RevocationStatus { crl, err := fetchCRL(c, crlVerifyCrt, cfg) if err != nil { - // We couldn't load any CRL files for the certificate, so we don't know if it's RevocationUnrevoked or not. + // We couldn't load any CRL files for the certificate, so we don't know + // if it's RevocationUnrevoked or not. This is not necessarily a + // problem - it's not invalid to have no CRLs if you don't have any + // revocations for an issuer. We just return RevocationUndetermined and + // there is a setting for the user to control the handling of that. grpclogLogger.Warningf("fetchCRL(%v) err = %v", c.Issuer, err) return RevocationUndetermined } diff --git a/security/advancedtls/crl_test.go b/security/advancedtls/crl_test.go index a674d4051be9..f76ae5739a63 100644 --- a/security/advancedtls/crl_test.go +++ b/security/advancedtls/crl_test.go @@ -509,6 +509,11 @@ func TestRevokedCert(t *testing.T) { revokedLeafChain := makeChain(t, testdata.Path("crl/revokedLeaf.pem")) validChain := makeChain(t, testdata.Path("crl/unrevoked.pem")) cache, err := lru.New(5) + cRLProvider := MakeStaticCRLProvider() + for i := 1; i <= 6; i++ { + crl := loadCRL(t, testdata.Path(fmt.Sprintf("crl/%d.crl", i))) + cRLProvider.AddCRL(crl) + } if err != nil { t.Fatalf("lru.New: err = %v", err) } @@ -567,7 +572,7 @@ func TestRevokedCert(t *testing.T) { } for _, tt := range revocationTests { - t.Run(tt.desc, func(t *testing.T) { + t.Run(fmt.Sprintf("%v with x509 crl hash dir", tt.desc), func(t *testing.T) { err := CheckRevocation(tt.in, RevocationConfig{ RootDir: testdata.Path("crl"), AllowUndetermined: tt.allowUndetermined, @@ -580,6 +585,18 @@ func TestRevokedCert(t *testing.T) { t.Error("Unrevoked certificate not allowed") } }) + t.Run(fmt.Sprintf("%v with static provider", tt.desc), func(t *testing.T) { + err := CheckRevocation(tt.in, RevocationConfig{ + AllowUndetermined: tt.allowUndetermined, + CRLProvider: cRLProvider, + }) + t.Logf("CheckRevocation err = %v", err) + if tt.revoked && err == nil { + t.Error("Revoked certificate chain was allowed") + } else if !tt.revoked && err != nil { + t.Error("Unrevoked certificate not allowed") + } + }) } } @@ -627,6 +644,7 @@ func setupTLSConn(t *testing.T) (net.Listener, *x509.Certificate, *ecdsa.Private } // TestVerifyConnection will setup a client/server connection and check revocation in the real TLS dialer +// TODO add CRL provider tests here? func TestVerifyConnection(t *testing.T) { lis, cert, key := setupTLSConn(t) defer func() { From c88d12da96b5f68ed1e32b3fad4ce5e1c28fa9d4 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 26 Sep 2023 21:15:57 +0000 Subject: [PATCH 12/62] New certs to be used for CRL tests. Added test for passing and failing connections based on CRL check outcomes --- security/advancedtls/advancedtls_test.go | 64 +- security/advancedtls/go.sum | 1435 +++++++++++++++++ .../internal/testutils/testutils.go | 22 + .../testdata/crl/provider/README.md | 14 + .../testdata/crl/provider/client_cert.key | 52 + .../testdata/crl/provider/client_cert.pem | 32 + .../crl/provider/client_trust_cert.pem | 32 + .../crl/provider/client_trust_key.pem | 52 + .../advancedtls/testdata/crl/provider/crl.cnf | 16 + .../testdata/crl/provider/crl_empty.pem | 20 + .../crl/provider/crl_server_revoked.pem | 20 + .../testdata/crl/provider/crlnumber.txt | 1 + .../testdata/crl/provider/extensions.conf | 4 + .../testdata/crl/provider/index.txt | 1 + .../testdata/crl/provider/server_cert.key | 52 + .../testdata/crl/provider/server_cert.pem | 32 + .../crl/provider/server_trust_cert.pem | 32 + .../crl/provider/server_trust_key.pem | 52 + 18 files changed, 1929 insertions(+), 4 deletions(-) create mode 100644 security/advancedtls/testdata/crl/provider/README.md create mode 100644 security/advancedtls/testdata/crl/provider/client_cert.key create mode 100644 security/advancedtls/testdata/crl/provider/client_cert.pem create mode 100644 security/advancedtls/testdata/crl/provider/client_trust_cert.pem create mode 100644 security/advancedtls/testdata/crl/provider/client_trust_key.pem create mode 100644 security/advancedtls/testdata/crl/provider/crl.cnf create mode 100644 security/advancedtls/testdata/crl/provider/crl_empty.pem create mode 100644 security/advancedtls/testdata/crl/provider/crl_server_revoked.pem create mode 100644 security/advancedtls/testdata/crl/provider/crlnumber.txt create mode 100644 security/advancedtls/testdata/crl/provider/extensions.conf create mode 100644 security/advancedtls/testdata/crl/provider/index.txt create mode 100644 security/advancedtls/testdata/crl/provider/server_cert.key create mode 100644 security/advancedtls/testdata/crl/provider/server_cert.pem create mode 100644 security/advancedtls/testdata/crl/provider/server_trust_cert.pem create mode 100644 security/advancedtls/testdata/crl/provider/server_trust_key.pem diff --git a/security/advancedtls/advancedtls_test.go b/security/advancedtls/advancedtls_test.go index afad25e7cb4b..da5ac2c783a8 100644 --- a/security/advancedtls/advancedtls_test.go +++ b/security/advancedtls/advancedtls_test.go @@ -371,6 +371,31 @@ func (s) TestClientServerHandshake(t *testing.T) { getRootCAsForServerBad := func(params *GetRootCAsParams) (*GetRootCAsResults, error) { return nil, fmt.Errorf("bad root certificate reloading") } + + getRootCAsForClientCRL := func(params *GetRootCAsParams) (*GetRootCAsResults, error) { + return &GetRootCAsResults{TrustCerts: cs.ClientTrust3}, nil + } + + getRootCAsForServerCRL := func(params *GetRootCAsParams) (*GetRootCAsResults, error) { + return &GetRootCAsResults{TrustCerts: cs.ServerTrust3}, nil + } + + makeStaticCRLProvider := func(containsRevoked bool) *RevocationConfig { + cRLProvider := MakeStaticCRLProvider() + var crl *CRL + if containsRevoked { + crl = loadCRL(t, testdata.Path("crl/provider/crl_server_revoked.pem")) + } else { + crl = loadCRL(t, testdata.Path("crl/provider/crl_empty.pem")) + } + cRLProvider.AddCRL(crl) + + return &RevocationConfig{ + AllowUndetermined: true, + CRLProvider: cRLProvider, + } + } + cache, err := lru.New(5) if err != nil { t.Fatalf("lru.New: err = %v", err) @@ -594,14 +619,14 @@ func (s) TestClientServerHandshake(t *testing.T) { // server custom check fails { desc: "Client sets peer cert, reload root function with verifyFuncGood; Server sets bad custom check; mutualTLS", - clientCert: []tls.Certificate{cs.ClientCert1}, - clientGetRoot: getRootCAsForClient, + clientCert: []tls.Certificate{cs.ClientCert3}, + clientGetRoot: getRootCAsForClientCRL, clientVerifyFunc: clientVerifyFuncGood, clientVType: CertVerification, clientExpectHandshakeError: true, serverMutualTLS: true, - serverCert: []tls.Certificate{cs.ServerCert1}, - serverGetRoot: getRootCAsForServer, + serverCert: []tls.Certificate{cs.ServerCert3}, + serverGetRoot: getRootCAsForServerCRL, serverVerifyFunc: verifyFuncBad, serverVType: CertVerification, serverExpectError: true, @@ -704,6 +729,37 @@ func (s) TestClientServerHandshake(t *testing.T) { Cache: cache, }, }, + // Client: set valid credentials with the revocation config + // Server: set valid credentials with the revocation config + // Expected Behavior: success, because non of the certificate chains sent in the connection are revoked + { + desc: "Client sets peer cert, reload root function with verifyFuncGood; Server sets peer cert, reload root function; Client uses CRL; mutualTLS", + clientCert: []tls.Certificate{cs.ClientCert3}, + clientGetRoot: getRootCAsForClientCRL, + clientVerifyFunc: clientVerifyFuncGood, + clientVType: CertVerification, + clientRevocationConfig: makeStaticCRLProvider(false), + serverMutualTLS: true, + serverCert: []tls.Certificate{cs.ServerCert3}, + serverGetRoot: getRootCAsForServerCRL, + serverVType: CertVerification, + }, + // Client: set valid credentials with the revocation config + // Server: set revoked credentials with the revocation config + // Expected Behavior: fail, server creds are revoked + { + desc: "Client sets peer cert, reload root function with verifyFuncGood; Server sets revoked cert; Client uses CRL; mutualTLS", + clientCert: []tls.Certificate{cs.ClientCert3}, + clientGetRoot: getRootCAsForClientCRL, + clientVerifyFunc: clientVerifyFuncGood, + clientVType: CertVerification, + clientRevocationConfig: makeStaticCRLProvider(true), + serverMutualTLS: true, + serverCert: []tls.Certificate{cs.ServerCert3}, + serverGetRoot: getRootCAsForServerCRL, + serverVType: CertVerification, + serverExpectError: true, + }, } { test := test t.Run(test.desc, func(t *testing.T) { diff --git a/security/advancedtls/go.sum b/security/advancedtls/go.sum index 2766a3d8223a..99259aed2d05 100644 --- a/security/advancedtls/go.sum +++ b/security/advancedtls/go.sum @@ -1,22 +1,1457 @@ +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= +cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= +cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= +cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= +cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= +cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= +cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= +cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= +cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= +cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= +cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= +cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= +cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= +cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= +cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= +cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= +cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= +cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= +cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= +cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= +cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= +cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= +cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= +cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= +cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= +cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= +cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= +cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= +cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= +cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= +cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= +cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= +cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= +cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= +cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= +cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= +cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= +cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= +cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= +cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= +cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= +cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= +cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= +cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= +cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= +cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= +cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= +cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= +cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= +cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= +cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= +cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= +cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= +cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= +cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= +cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= +cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= +cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= +cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= +cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= +cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= +cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= +cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= +cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= +cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= +cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= +cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= +cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= +cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= +cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= +cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= +cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= +cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= +cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= +cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= +cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= +cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= +cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= +cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= +cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= +cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= +cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= +cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= +cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= +cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= +cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= +cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= +cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= +github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= +github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= +golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= +google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= +google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= +google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= +google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= +google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= +google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= +google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= +google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= +google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= +google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= +google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= +google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= +modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= +modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= +modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= +modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= +modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= +modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= +modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= +modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= +modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= +modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= +modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= +modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/security/advancedtls/internal/testutils/testutils.go b/security/advancedtls/internal/testutils/testutils.go index 1bc0dc3bf4e2..722a72ca108b 100644 --- a/security/advancedtls/internal/testutils/testutils.go +++ b/security/advancedtls/internal/testutils/testutils.go @@ -35,12 +35,18 @@ type CertStore struct { // ClientCert2 is the certificate sent by client to prove its identity. // It is trusted by ServerTrust2. ClientCert2 tls.Certificate + // ClientCert3 is the certificate sent by client to prove its identity. + // It is trusted by ServerTrust3. Used in CRL tests + ClientCert3 tls.Certificate // ServerCert1 is the certificate sent by server to prove its identity. // It is trusted by ClientTrust1. ServerCert1 tls.Certificate // ServerCert2 is the certificate sent by server to prove its identity. // It is trusted by ClientTrust2. ServerCert2 tls.Certificate + // ServerCert3 is a revoked certificate + // (this info is stored in crl_server_revoked.pem). + ServerCert3 tls.Certificate // ServerPeer3 is the certificate sent by server to prove its identity. ServerPeer3 tls.Certificate // ServerPeerLocalhost1 is the certificate sent by server to prove its @@ -51,10 +57,14 @@ type CertStore struct { ClientTrust1 *x509.CertPool // ClientTrust2 is the root certificate used on the client side. ClientTrust2 *x509.CertPool + // ClientTrust3 is the root certificate used on the client side. + ClientTrust3 *x509.CertPool // ServerTrust1 is the root certificate used on the server side. ServerTrust1 *x509.CertPool // ServerTrust2 is the root certificate used on the server side. ServerTrust2 *x509.CertPool + // ServerTrust2 is the root certificate used on the server side. + ServerTrust3 *x509.CertPool } func readTrustCert(fileName string) (*x509.CertPool, error) { @@ -79,12 +89,18 @@ func (cs *CertStore) LoadCerts() error { if cs.ClientCert2, err = tls.LoadX509KeyPair(testdata.Path("client_cert_2.pem"), testdata.Path("client_key_2.pem")); err != nil { return err } + if cs.ClientCert3, err = tls.LoadX509KeyPair(testdata.Path("crl/provider/client_cert.pem"), testdata.Path("crl/provider/client_cert.key")); err != nil { + return err + } if cs.ServerCert1, err = tls.LoadX509KeyPair(testdata.Path("server_cert_1.pem"), testdata.Path("server_key_1.pem")); err != nil { return err } if cs.ServerCert2, err = tls.LoadX509KeyPair(testdata.Path("server_cert_2.pem"), testdata.Path("server_key_2.pem")); err != nil { return err } + if cs.ServerCert3, err = tls.LoadX509KeyPair(testdata.Path("crl/provider/server_cert.pem"), testdata.Path("crl/provider/server_cert.key")); err != nil { + return err + } if cs.ServerPeer3, err = tls.LoadX509KeyPair(testdata.Path("server_cert_3.pem"), testdata.Path("server_key_3.pem")); err != nil { return err } @@ -97,11 +113,17 @@ func (cs *CertStore) LoadCerts() error { if cs.ClientTrust2, err = readTrustCert(testdata.Path("client_trust_cert_2.pem")); err != nil { return err } + if cs.ClientTrust3, err = readTrustCert(testdata.Path("crl/provider/client_trust_cert.pem")); err != nil { + return err + } if cs.ServerTrust1, err = readTrustCert(testdata.Path("server_trust_cert_1.pem")); err != nil { return err } if cs.ServerTrust2, err = readTrustCert(testdata.Path("server_trust_cert_2.pem")); err != nil { return err } + if cs.ServerTrust3, err = readTrustCert(testdata.Path("crl/provider/server_trust_cert.pem")); err != nil { + return err + } return nil } diff --git a/security/advancedtls/testdata/crl/provider/README.md b/security/advancedtls/testdata/crl/provider/README.md new file mode 100644 index 000000000000..0e5b9b6d7e07 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/README.md @@ -0,0 +1,14 @@ +openssl req -x509 -newkey rsa:4096 -keyout server_trust_key.pem -out server_trust_cert.pem -days 365 -subj "/C=US/ST=VA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.ca.com" -nodes +openssl req -x509 -newkey rsa:4096 -keyout client_trust_key.pem -out client_trust_cert.pem -days 365 -subj "/C=US/ST=CA/L=SVL/O=Internet Widgits Pty Ltd" -nodes + +openssl req -newkey rsa:4096 -keyout server_cert.key -out new_cert.csr -nodes -subj "/C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com" -sha256 +openssl x509 -req -in new_cert.csr -out server_cert.pem -CA client_trust_cert.pem -CAkey client_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile extensions.conf + +openssl req -newkey rsa:4096 -keyout client_cert.key -out new_cert.csr -nodes -subj "/C=US/ST=CA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.com" -sha256 +openssl x509 -req -in new_cert.csr -out client_cert.pem -CA server_trust_cert.pem -CAkey server_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile extensions.conf + +#extensions.conf +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +basicConstraints = CA:FALSE +keyUsage = digitalSignature, keyEncipherment \ No newline at end of file diff --git a/security/advancedtls/testdata/crl/provider/client_cert.key b/security/advancedtls/testdata/crl/provider/client_cert.key new file mode 100644 index 000000000000..8049c22fdbf3 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/client_cert.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +wXbbl1Q950WxvkFwHVPEf0AjcNQ+vkE= +-----END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider/client_cert.pem b/security/advancedtls/testdata/crl/provider/client_cert.pem new file mode 100644 index 000000000000..ea76996a1f84 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/client_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFmTCCA4GgAwIBAgIUKoNorgc78v8NhX5pT0eX29QTlxQwDQYJKoZIhvcNAQEL +BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQxGzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTAe +Fw0yMzA5MjYwNDM5MTlaFw0zMzA5MjMwNDM5MTlaMFcxCzAJBgNVBAYTAlVTMQsw +CQYDVQQIDAJDQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRgw +FgYDVQQDDA9mb28uYmFyLmhvby5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDE6zfvwT6gRaNXc4sNpDqRQR9lHU53fh8cEuYN0tk9iO7aNJhXWOzx +/UTk5ccf9On91pIUQ1Px+crZvv5wHQXTH0ng4jgKe49tNzi23O7IqwPnTOS0v6Zl +pGzJprrMee+YP34SpbZLBwZW9oj1q0vr12pir35B6yTbJGOxRQXaN7302Knjorvj +GeETgIuDiqHv5swRbiOQS1JfquhHTOPJKWq2uK0r9C31euQiT0u0JB7XHcCJZQJ/ +5ByG7I6N+rysU9u7CHgto92cjz7Oe5Fk4LrKj62bmL6EJMsucZGagtSnuXuuPcCm +BDRzNjjvp1/Mym4Fq5GmpOyRiNXXJgdqhR59uKc0Ed9t8Zg3gHT9kDb4/ubHe3ZY +2gPOSO4QJEt+mBWmV9MN9X7nOk+RjKY9TipMQ0+Tc3oZTdUMNohhUwi0GPuEEjbG +rvIIkpYtTKliLBZI8Nb/F3ibJeODA3NzcUDsTBRk7W6xW7qJSS9Bcl/9xVS5+wgC +JHZAuSCRw6yRPrO56CQqeHrKb+3H+oXHwyt/jdYQWL6qHeo6YCg4EV/C2q5R5crd +l4AhjZOm1Y03EbfbRcn4DntjFpkUkGfAMkSczRGH264DIuTLZ1c0zSM/4GoGbGrw +XbiYi89bzgI7rgGQ7xEpigJT0o6RGA3u6SI/5GHFqcHs5Vou8nbUTQIDAQABo1ow +WDAdBgNVHQ4EFgQUyf6qal9fKpoR1+GMfnFCzoK+ClcwHwYDVR0jBBgwFoAUDjmd +nNwr6MWPMhPftdbsTkQbdG8wCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwDQYJKoZI +hvcNAQELBQADggIBAA+ALoE20z6k0rt5tc1V8OvTav5igJxcO9+gc4G0j+SL6RO+ +Z/uv2PTsR8Lyr0TYaqZQRANDA/hEXJK1UPGlFoVtAPTwLtasrSkvPPFS3puTv7Os +sXODpZsUHHouTQQHiMktCQLwI6W+3B/pH/YMx+vFnf+GQZ+JTfJXzv8UPrtVngU2 +kvbIUd6csWjp1n6KniUf44t6s7n+1XXo3Njaifwx6bLMTAcPyqdzg87VhyDCb0nA ++cJUl/sq38Axj6P5fNh22g8HJXFA0yhJXqIOvGUOikc3Hq4Uyd6gEpisP8dnrhu3 +Pucorv3ccLGJV2Vg+NI0fV/94dX9Yv09Sv0B23uCQqtATdLpL6ZBANn+lX15MkJP +E8kPMEvmi3qRP2lqbssgiyhcHUE0XIE9o/fYnwk8wfMtaS0XPSOWzPddzqjiNqDB +1vsHCWLLBDOvz0nFd5L5P/uzP1M72mfSWbndJnTTQXLOJKpeubyOlFDBLqE399wP +p6H/lsz9y/Qp299CiSpzdI0nWO2KHYbikTt4LicKz2VMRSytpAV0V6AHKC2cZmUz +589Gfn/pgtJ/c2ULRiTDbpnhsRtHWE/oWk/dxZHzs+Cd04fH7+3gxVDGYioF9YUH +nlvnvl+OsEdFckYfVq1zaSIHqKNBJUhf3CEtPmiyZ1a9zc+uwLpd4dPsh7v3 +-----END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider/client_trust_cert.pem b/security/advancedtls/testdata/crl/provider/client_trust_cert.pem new file mode 100644 index 000000000000..8f33cc3c0081 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/client_trust_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFdzCCA1+gAwIBAgIUTzuP0DCVAESFS7E3nDyJIKuDPjowDQYJKoZIhvcNAQEL +BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf +BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA5MjYwMzQ5NTla +Fw0yNDA5MjUwMzQ5NTlaMEsxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoG +A1UEBwwDU1ZMMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6DLiFSsZNQlctTUKjawO47LRt +Gg4fyerF7TP/+E+eWRxElBDoo7mq3va+x+A4FT9Xqoe20+ZSpXYCEhcATw1JEuXi +j2fPf5p2sHeWnv70/7aMeBhvzfh++qrup5LtwnASqYh6j05wOQFAecdqOlPTT9k9 +39RQmUA6GNoMpv3NjToUM/9MgpvIEKBuiDt/hSUupkTz6P1T1RctKT0AFIA8Z3bJ +n8tbSo0H3R7TGQSIuIzzaFlMrZBOEiwURiX9q8YjVPgw2mZ4d/vDOBvWZxQ04Er8 +Uvkq2eHZdQvfqvkqwwdri+rccI407iCg86XfwaMOt+W+p43SOB+fPa+IB58fvMYt +UtJalpjfEoOSRPrj+c6jRmJC7Ox3hWERHujNfncDf5ezpZNGicjDGyDafOjuButC +4Emq/0Ur/qi9+612wZwBDk3zP0SrhdXVBqfQVKnfm8bLDbV9HmatWTSu3e8zHsIK +RV+4dWZ7D/FtFtrjJGRdwVTN1nQ0XL9jwcXIlb+ADP4aSouHcSo+DJvl4tFYCoQy +icNz3/4UuWvJwSt5wq0OB8Q2yyaYm5+P7dYthBT4VAQNLdPQ4KKy/RyP15EE8Vpq +dECSi6BgiGjeX098qQU/qAzYtTFnbZJeI0u6C5sKDZgRK1RgBo2KZQX11c8P740I +hJhChi/Tv5ciembUfQIDAQABo1MwUTAdBgNVHQ4EFgQUxObRG/aeifFAEvKwMrL2 +7uUMRU4wHwYDVR0jBBgwFoAUxObRG/aeifFAEvKwMrL27uUMRU4wDwYDVR0TAQH/ +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAjA2Wjwlarht6oGUGw7HY98uAC8gt +w/6m4wRWOkEJkGqW004eNnMqjt7OweJPBviRZafpXjMJE9yy/wGCsSWPpuC7+Akq +j/Xr0npSPS7UlvP0fvSA2a6WM3022tZhj0iWEf32w/m04JthRNNfznYT0KjUDosJ +U36MtesrUTytl6UIW9KSgWQ+jc+rVWwFjL/zCDuoDVsBT/WeHerJghpWp0XHCBxg +3E8ZCex9/u8eWl8YisMLbluV0QuCb7Fa5ybsb0xUShgYiHzj2kaRRB8Dg8ftKhXZ +DLp2b/rGs2F83i9S03DezbTH1zfw/ffjFO/ksxLCsn8xALEt8HbAu9sPNxMPRxgi +y5zIwU4eRjATseArO2cjMWjAwZVu49GjGWtSps1Azx1wUaHh8gbdypamyv8YP/Ie +NTUwZxbwTucMq3TGGOWApeQSFWPA5Sb2w2dBZEZWXX3dp6wVc+EY/C0opiSaUKw9 +S+bpFPgi17pn2wYhtb9Jpo5DJm/iAmH7WrH6hJPfLmg4n3RG1ihsO9LaazQMIknb +DBkjprjnLN+a/YtFukd+XkZ1xuVbTRjYrV7QOJFzidWW5l49xx0ZTvgXbCKj5Wl4 +ZqqU+z8KykBYaC85aBVbVwsbYhRLOilQrbtFNOgQFDnOq4tPjZVXsw/9hgC3FChr ++RQG8YEclj/l45k= +-----END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider/client_trust_key.pem b/security/advancedtls/testdata/crl/provider/client_trust_key.pem new file mode 100644 index 000000000000..f1aa8ad16119 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/client_trust_key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +/QygMqmjwwSOJi4P5uS604XabnKDo8V4 +-----END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider/crl.cnf b/security/advancedtls/testdata/crl/provider/crl.cnf new file mode 100644 index 000000000000..8d0124557043 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/crl.cnf @@ -0,0 +1,16 @@ +[ ca ] +default_ca = my_ca + +[ my_ca ] +crl = crl.pem +default_md = sha256 +database = index.txt +crlnumber = crlnumber.txt +default_crl_days = 30 +default_crl_hours = 1 +crl_extensions = crl_ext + +[crl_ext] +# Authority Key Identifier extension +authorityKeyIdentifier=keyid:always,issuer:always + diff --git a/security/advancedtls/testdata/crl/provider/crl_empty.pem b/security/advancedtls/testdata/crl/provider/crl_empty.pem new file mode 100644 index 000000000000..e6742302111f --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/crl_empty.pem @@ -0,0 +1,20 @@ +-----BEGIN X509 CRL----- +MIIDMTCCARkCAQEwDQYJKoZIhvcNAQELBQAwSzELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZBcNMjMwOTI2MTU0ODExWhcNMjMxMDI2MTY0ODExWqCBmTCBljCBhgYD +VR0jBH8wfYAUxObRG/aeifFAEvKwMrL27uUMRU6hT6RNMEsxCzAJBgNVBAYTAlVT +MQswCQYDVQQIDAJDQTEMMAoGA1UEBwwDU1ZMMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGSCFE87j9AwlQBEhUuxN5w8iSCrgz46MAsGA1UdFAQEAgIQ +ATANBgkqhkiG9w0BAQsFAAOCAgEAMmcb01RBSBZ1/r7gIceXSB0WftdHgWQ3i+pe +kWdX6kxX/SBR1cd47ygdBSyDaRD2HUzwXAMa4icGFPZ33dtGnkoHjNxUMkK3O+2c +RArJFShXKRatvJgo/4peQx0/2Ho3dCNsrTbcbgQhMgHoRg4Rpgqp6KPFMEN6xysb +5ZJ75Pgp0wHAgcviq7zvoEObqoFX3CPeZ2ZYXO3qQElGFARg3K+2h5bugN7pJqWi +kVjfhf4bjkx2bt/4KDczatjvLk1FF6Mnlx9EWEABijelARI7l002RSrjkOsDa5Ya +vyLIT+6+wcb2qzJ4+ffo+U0Xpy4ZfKDnts2bXs997ro98KDutuJvEOhEcuNqoYxf +3qbzjeNJ82F8LcDHa89suNmqcX1lecEA6tM+kRXsuuQWuNczxDQ+t35jJ8QD0hD4 +QX36JdZsk4Zqffunhm9vhciK2P4YvlAam1bC/fRVUzCnEaeTFXBbpZZfi2+gSJYw +PfLUlvVhICrQVCq2fMEvgaOreuL6prIBKSXDwA/fS1pV54bf9r9a5aBoJ4sYTPkx +4Y0ld8nmRLD5wPPs8ItIRReY2lqz2yR/ocEWbuFN0RqZItPCIsnaujWpF2a0UZeA +rlEW7RpPA8unGy9RasCq42Y4BOkmyF02Dudozvn01mGtWP6UwUyvEhkKIv7yc5ny +KVPnhaE= +-----END X509 CRL----- diff --git a/security/advancedtls/testdata/crl/provider/crl_server_revoked.pem b/security/advancedtls/testdata/crl/provider/crl_server_revoked.pem new file mode 100644 index 000000000000..c606b13ff2d7 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/crl_server_revoked.pem @@ -0,0 +1,20 @@ +-----BEGIN X509 CRL----- +MIIDWjCCAUICAQEwDQYJKoZIhvcNAQELBQAwSzELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZBcNMjMwOTI2MTYwMDI0WhcNMjMxMDI2MTcwMDI0WjAnMCUCFAHXnr3n +I/bocpxh4dhO2W7PGVGMFw0yMzA5MjYxNTU5NTBaoIGZMIGWMIGGBgNVHSMEfzB9 +gBTE5tEb9p6J8UAS8rAysvbu5QxFTqFPpE0wSzELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZIIUTzuP0DCVAESFS7E3nDyJIKuDPjowCwYDVR0UBAQCAhACMA0GCSqG +SIb3DQEBCwUAA4ICAQA0zQRRPiEy2spIqiidE+10NAiKbbpGCfH7ZcUaxHeBoWTJ +bDU/cNqrOUISqkAR0npWwsTI8KADSv39vXXpush6FfabixeB53Och6S2Y5UGsLVE +6UCqcEOSvh42kIO5PiaLQJrFsFp1c1zWS4AVkPJTcjMTbev2cbPW88wRaRJ/GDKp +nfr0OTWY2PcSjaYjDmcTMO5s8kEQDAoNvhhTA2UUHZKuafcnjsgk3eOu62IXQQbP +fcKIKxSfJgGDgrCxIsKrKoH12gVcbEY7pupL9IdQSihf6ROW+9YphfzrzwL8/V+E +w5nZrd/DTrRSUQl+JcJ74xsjgX4PRCfbXM9FFzVuhD26sKJHwL8NUTwMmydrydF6 +/1k3/K1bYf2+L0y4QN0XNTgfMUoRasPVv4bywg6bpPNXKijVKCyFxWFMrk/6ro5d +pDGYXco3l2g/mbINDT2piZWnBCDxI53wFtVfG99hXH3akSygeEt20cZT+qhNZ9eR +YIibCxWJYQ5xI/hTsoC0YFMirQsoI/CVZEkMOe/L44j5RdTdnemI3yvPanwy2s2l +zFWln9myyy7/OLEkEaYtNfZuCJId00ZHLdMmtpCN+woeiWuyWqext8pmTkMUousb +etIbd596MoTAXKg5AjimRzsaXASxpM6lhjvsBOD2iQ+VI94DQVSVj78lp61x1A== +-----END X509 CRL----- diff --git a/security/advancedtls/testdata/crl/provider/crlnumber.txt b/security/advancedtls/testdata/crl/provider/crlnumber.txt new file mode 100644 index 000000000000..baccd0398f98 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/crlnumber.txt @@ -0,0 +1 @@ +1003 diff --git a/security/advancedtls/testdata/crl/provider/extensions.conf b/security/advancedtls/testdata/crl/provider/extensions.conf new file mode 100644 index 000000000000..8edcc568806b --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/extensions.conf @@ -0,0 +1,4 @@ +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +basicConstraints = CA:FALSE +keyUsage = digitalSignature, keyEncipherment \ No newline at end of file diff --git a/security/advancedtls/testdata/crl/provider/index.txt b/security/advancedtls/testdata/crl/provider/index.txt new file mode 100644 index 000000000000..62e53ee74054 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/index.txt @@ -0,0 +1 @@ +R 330923042937Z 230926155950Z 01D79EBDE723F6E8729C61E1D84ED96ECF19518C unknown /C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com diff --git a/security/advancedtls/testdata/crl/provider/server_cert.key b/security/advancedtls/testdata/crl/provider/server_cert.key new file mode 100644 index 000000000000..fcf5428820c9 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/server_cert.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQCYlPg91VB6jafM +CYZ6WCmM6yJjUgdeoj/L+fHrG2lqZCkB00OVPK/Ls6ABUmgokv5H8ucAN5YjjK2L +bAiAZ7E1k513Fl6OhtLdzsfmrct1mnonQbo2Bqw5ZT22Vl67hPJrcY04Ejh4sJbH +bmIV7tXN+dRY/dTyE+xejb4G4R+//VyDhEEO3HnioVlQH8HM1BpqbhjlrcQ07i5S +SVwondFJQ0XPx1jScz+ueVJQQpsqsTmH7KRXkSS6qfs2O2/r2mU4WMf+/2OXTnYG +uaK43XWkCcb+NejR0cZbMyE9z3CPnRosTGAog9zV9Ua/HCK8p9wzHfxyYmlhAl0F +9256KiRChnwKYHK1xC95AYJiPvdB4vPcPkec+TrxYYiPDeuGzsMkdR8k8GZGYDoR +B7fGyXSTQK1RX+xRax8IxrEzySAQnxikvux5g5NdxMEDkBF3UACJTfno4fVftc/Z +ri3pZFCsv70baKYLcNO25etu7hVgbZa+pTFWSJ39hc9iY1t3pQMa4EwWU4wByKcb +8ijwb8EQeo7GcFmvAdkhnMELmR52gJB8nDbB8jPP0AYS0YXCBkt/BITLqJyzen5A +qr7pn61TdRWErM3y94SWVgu6rzX6oHADsbIXNzNpbNxJXAHuOkjEffjeMiy/HGlr +Cuh//13yf4TAjBXBl4x6E8cBgLlNfwIDAQABAoICABbrdbiwFNU79yMuoR06pg8t +eO5rcYD1JVkqE/1pkjWxkvlbt1CxG4UhV90q+Etz/S9onOuK7gosBRum4EYNjXCL +WoF7gjuto/o+qAeOPrxNNqEz/dlEulUq0JDVu4g2icW6+mgcUJBMknGlsqUxybsV +lqDBjF3KwVxYtV1KLt9Dm0SENtOZKdPxwb1H7/ApQkiPfjzuE8WRq7Kfo3DUeA4l +xVRXUQ/X0Q6P2d4Fi1Jn6cjxsqQWwOorQ3mbhw3qMf87UtuKmHavigork3ZqidPY +xiQEjBMMjFvBu/IReYqRIk/aLy2W1TLsk7J8cvlcy7aAAB5cixDP7pJJXzaJowWB +yPHcK9F2To040KGVwc/N+zdy4rG6TR3bLVpJ5OtjvLR4HjgGGgWcvuFROl2sBsU/ +I0K98BAjqTWVHidcp/6StSHBnL7zWYas4tob15qUOifvbkbrDXyDRQ//WKPoI9A7 +/7f6YJlA9UZrIyph9ceVxkTvuk1NiRstQqGYXOaPRNtyiskE/Y2+d6rPluLly+5N +hmUMskovZSz169mlxpkpikYizBiPx5b1RLuPf4ML7mzg4JjCHLEj8c3zBPapsjIy +QbkDPmgV03DdYfk2BTvCHeMsKJ6OOupBt6NwYoi0/ulKCkTkT0B3H6y28LL4fgdp +OSaNlsxxnOaGeZwGUSDNAoIBAQDOOiYJaK5zPpx57La8WvnJSk6jB+t+EBHUDL4s +1IhH3Top9HYcvFLggpGqneFFp6+fQXpZNsDVrE2BYksNuVQq91AfrBfVCe/x/gOt +lw38R2vDHf8pAQ9Ld4ahKBueafBJBncUqDFpYDS0n1tsZEa4lGZ+A0pxj7+Du/YW +qTZCGwGX3ScR1qPdOKclz/yzEb1q+m80ivCMPsCWpx+6nVddLbMqQNgqURq+EjpK +TPvO2zWmiQjI4m+PmQp87FC3LAcI9B2Esl+lSXUWsUqyxJeyoyrXwr52m2h7wm3V +VdCp2bzVPLsu+rLHtETz2SLWN43BnTB7zxncyB4eQoa7zU/rAoIBAQC9aFKYctKd +dVcG0+1RMR20m9nFRKToexsLA6nkbQxexVo1QM8XA5UpPU0lfG4AZtqcAxxIn1Q8 +HTW3gBImIIFhJEUd+9UZhPf19TokdW8DbbLRd+dzlxY2Al9/D63LHAW8ZLPm91+I +t9nrqxUnlRsE1VabGeReobvEyd+aZcCpCuZlH4QB6vgCC2lPhXjPRPodM1sDz7WP +oxTKHTWJSBvRrovJdZKDbNYZb9N6NGeF66gAjOatMHzTFvycIqxKUvzPrWP1gVxr +gay9T0QhQOg0hz0mIhaxL9tFYkaPNyiQMlQMV9b17fYqV8V0caZVAaA4Yi3dZv2j +JWdnemcjSae9AoIBAQCnHQHDsBtlcgA04Pg3a/VsAxX0EK/pCLaZwfW9uD/zFDdU +1i98G7OBEO1fd+6blkW/ZLzzXGYpACYSSYeOSHcHFj/MWpU5YoMY8wEiAv0nSOzv +QPiKzrwJKINlRm4TEb/QPTfz+lOIE5jCITxhemTAoCjpxlrl6VmFM6Q4OsioCuBg +oqpNT0ScOJNiFcetiBuZe/TvfANvck54Ble0zlmEPUspW0CQEgb8rSKlIPsQZhx0 +pikDOFK054xTLOA/fqL4w3DEHhNZikwipmpksOBItuY5zyquTEL/vs9oB9E+Qh4+ +IaeBa0/CerdBbtxHAN+TXIB0Y5+OhG+7j6dEuhwfAoIBAQCIGZJgIQHeSxbBvT7l +/JkP0n1lFrI4q1z/SR6nGQSuhLkRUwR70QM5oubwbLzOZRnoyQNl27c3ivWyacCg +zPKqztkrdm3l7HoQrqmc0RV4LTc09SfHE2XOYV4leyqyq+wjN2leKZroLtPUWBbX +XnkPvAyDXtOOfEDkYvrdgwgxb7emKkM87Y5QScrebVYl+baPjPt1fadFEzY5FUwG +3V3ACeBAXs9syKP/my8mvMk2neMWoiiPgivvsrDJsNCEh6VJxZnyK4Yxafip/29B +RdaVCCiUYiJUJ8Mz1C5HcqcfFkr2o5e/F8KvImqsBY4zvvtWCJZUZVEUDiiDXp0F +6kMxAoIBACsOp4xKTaxilyjjIOAmY8X7mRDQhZGJRhpVGNLJ6DmG+y4MfSgepyRs +6J9GjZfVE0wrCtRQnkoHuDrzktc6GhBamfJL9aDQCktgScb8I4/VBxCKPCGg18hK +E1N7wgad9SrEKrgAKcrF3HVsx9zRSZ2WybL9psOXcZazz+HyyDWUsU7nfgrgdgy7 +mwS8yUyQpyjWwCxFFUPjG9IvrHFq5SR75/3Tw4P3Q8VqyGs21zkK40j9YXRGsWz9 +oCjC1YwHBnruPl+tfk//6johnxR/lesH+FxVodBiINA2HtSv+5/9UWR5c96/4S4a +9hhFTf+DrOyZ4lrtz2UjEPiueB2ZbFY= +-----END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider/server_cert.pem b/security/advancedtls/testdata/crl/provider/server_cert.pem new file mode 100644 index 000000000000..3fcb91d0d070 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/server_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFmjCCA4KgAwIBAgIUAdeevecj9uhynGHh2E7Zbs8ZUYwwDQYJKoZIhvcNAQEL +BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf +BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA5MjYwNDI5Mzda +Fw0zMzA5MjMwNDI5MzdaMGcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAG +A1UEBwwJRFVNTVlDSVRZMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBM +dGQxFDASBgNVBAMMC2Zvby5iYXIuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAmJT4PdVQeo2nzAmGelgpjOsiY1IHXqI/y/nx6xtpamQpAdNDlTyv +y7OgAVJoKJL+R/LnADeWI4yti2wIgGexNZOddxZejobS3c7H5q3LdZp6J0G6Ngas +OWU9tlZeu4Tya3GNOBI4eLCWx25iFe7VzfnUWP3U8hPsXo2+BuEfv/1cg4RBDtx5 +4qFZUB/BzNQaam4Y5a3ENO4uUklcKJ3RSUNFz8dY0nM/rnlSUEKbKrE5h+ykV5Ek +uqn7Njtv69plOFjH/v9jl052BrmiuN11pAnG/jXo0dHGWzMhPc9wj50aLExgKIPc +1fVGvxwivKfcMx38cmJpYQJdBfdueiokQoZ8CmBytcQveQGCYj73QeLz3D5HnPk6 +8WGIjw3rhs7DJHUfJPBmRmA6EQe3xsl0k0CtUV/sUWsfCMaxM8kgEJ8YpL7seYOT +XcTBA5ARd1AAiU356OH1X7XP2a4t6WRQrL+9G2imC3DTtuXrbu4VYG2WvqUxVkid +/YXPYmNbd6UDGuBMFlOMAcinG/Io8G/BEHqOxnBZrwHZIZzBC5kedoCQfJw2wfIz +z9AGEtGFwgZLfwSEy6ics3p+QKq+6Z+tU3UVhKzN8veEllYLuq81+qBwA7GyFzcz +aWzcSVwB7jpIxH343jIsvxxpawrof/9d8n+EwIwVwZeMehPHAYC5TX8CAwEAAaNa +MFgwHQYDVR0OBBYEFNfHmciiVzhqkHW8JLbtMgFNSc+1MB8GA1UdIwQYMBaAFMTm +0Rv2nonxQBLysDKy9u7lDEVOMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMA0GCSqG +SIb3DQEBCwUAA4ICAQAeK587sLTY3XQWKvIaJuWORsxAvhaEXHWTUJ+F0SHeeTdZ +Zgv0uDVQBmnwbndLy8/FKXPcJ2tKsUUCtu+/kCHKf89+yH1cRWU3PXfGAqqz61d1 +curWJw2NYRX9N+SmlcsaLtv1ekRnkJFytehKyINy2c1efL+MURI7kk7IuhwBhNNR +p44rN9On5AJ1TN63LfM48U899kaN/85pHEcFuh1QBfVKKXCzZ6JfE88eKHhN8Sd8 +alHoj9dlZboBqov1ehgrTuBgNR+mf9VkR31SPJe3valswpOwC9Smm3pOVSQW+GRb +tDRY4FExVNINCfwsYyPTJOys1AkYxz8J8cG96AYB11POZr4xniMoyPDdWuieDg7l +vImjlA3ZVAtIi1XWiWvMARTeV2hskVMVZdu62T2p1wzysxAa/L3K4jD0v9X+ID8o +0wREtTVpK7bfob5plbxEJMlOsBi2XWILRQNx5UsMAyBA8c3Ey47dTrJOOFHAx7dK +eUkztawJ3qxzwv6doUTtox8KlEZLijxuA302oQCb/JhuH4VweIUvbYvpbQ+1QKGo +Uqu1LcXj77hQmPp8Iqp4ygcXbKOIIIqNPQu4rcrAhgEAm7Qgv3hRDSYqNwA3e+MW +E9U/iK8rxaYZ7piVBruGHVI3+dNve9GgoINmWR/KTZnlEWBiX1yd0LVkoxBrPw== +-----END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider/server_trust_cert.pem b/security/advancedtls/testdata/crl/provider/server_trust_cert.pem new file mode 100644 index 000000000000..23b5cba665e1 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/server_trust_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFlTCCA32gAwIBAgIUD9Cq8x4SnbHZnInvmS1GtsPPtDwwDQYJKoZIhvcNAQEL +BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQxGzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTAe +Fw0yMzA5MjYwMzI2NTdaFw0yNDA5MjUwMzI2NTdaMFoxCzAJBgNVBAYTAlVTMQsw +CQYDVQQIDAJWQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRsw +GQYDVQQDDBJmb28uYmFyLmhvby5jYS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQC3ZtNlVOJz6fz7u+f1W851eUC6YviTNMNdOmsPlE2brQCHTO+b +EJ+1hyY/3v4ZRD1JtQ44Edic+7Bqnr0+EGMHaFaBE5R8InIQbKGSc869UpnZgYrI +Js1Q3icLuWOG8/Ll3hwQLe3P+I8WP5eA1xxZzwbRbSPGx28a2P5eXPOAy8MUzA6G +Hz4YoUNND+0x0sCQUXFVj3+NYCn6GGqwRUj1IfVLA6zpiwNdT5OYDwdEgEJKfhai +ZPcemIV08YgbreKPo52fzuWaS8WaaTNCLGHTuRCMMjOPssRdP9EzbIrTYxDQPRbN +uXPexjP8MWYGtDQqZYqafhDm6egk06Ihz/S7K8vXT3NnB7SDo5/afqBvjp8KCyuI +JPaL6c7mfNiD/dIc93uIdsIz8xbt3W4diZQeHPL8mapnPXYETV2T0U7/G+F/iprH +dCJIPVECGA1BAYnQ5mruFhsbzzVT/fELdRYnXHwxqQr689ChSz4O2mPBk7ECMt+4 +bjmx9mDGISM2sSxTfJ7EjEoz2lxB36OJTQt5OCar5Rv6Gw6eLuW1VvWRiI5QTqAw +LL4tM+bba9P4Zf8ZcHyZtHjpKT5JHcSfiGfpdT/p1YFe7dkVw/uTT/3vIDFgeoeL +ZS6FqAOj7UfIV4L94r4f9ZclyDGuxsMgW3io45H77FVwQ86Q6EMKA/PCawIDAQAB +o1MwUTAdBgNVHQ4EFgQUDjmdnNwr6MWPMhPftdbsTkQbdG8wHwYDVR0jBBgwFoAU +DjmdnNwr6MWPMhPftdbsTkQbdG8wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B +AQsFAAOCAgEASz+WIWtN1piSwUpQ5xmL67q8MK11fEYR4JeeCSbNIz3E/6ExXwLh +m+k/sKuAR0Ab2YRry1gi2AiFLo4rsmkdSNegcndNI4OPiixJyEn1ZwJOaXziAqMH +E1yuwpWQmVyiHurVyILvcD4z/FD6HpVb3AEd09wlnUeLTj0GA3F3k8Q5wb7L33TQ +gD3rfJiR9kTHVCoaZdJlaEZH6WPs5xSLY+tYK8X3+jrglqTB5IDO2I2rr0ySoGDM +I6VS4ZkCpptfDw164Nd8EmL+T3iPFMgJYAUVKvtV3+YnmNz6qaHQDYRTXgdZNA0F +LVyYaxE4UIpm9I9eG1fZat2hOVZUuz8Nr9maeg33IrPOxji9Q9hjL5lG0l+Odqjj +17uQpBew+Ql3EXZ3Nk8RgDvHnl0vWPk0k/KoPbSGQdUFAtF/Rsv8CMEzK4wmtLhX +J0d5uGIX1X5R/mhAm6jl9crnw7XyLbd/3NfoqhKeFN+hEZ0TL9CUTth+O3y2Vj+A ++QNMBopGunnEsKbwfwjUIjqt0z2oH16pn/KFPxF8U+t7x2oYMDNk/8M6xVFkH8v9 +gvuoTt+SoES6+qeU8nAD475SDp0JIaBmaugTkIa7RwsgHNGN/SVzfXcT6J2ht8Wj +o3SbSVt2T1KTGoaOpTv3FRzcwpWnqrOu7ZHxTco37NMTtKqQX6FaEFI= +-----END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider/server_trust_key.pem b/security/advancedtls/testdata/crl/provider/server_trust_key.pem new file mode 100644 index 000000000000..635c186fe298 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/server_trust_key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +rUkotyfd34kJTx26P3/2MF5JtQqJVSCW +-----END PRIVATE KEY----- From 1feaae3b5cddcc49b18803524a1dfab5398dd469 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Thu, 28 Sep 2023 22:23:41 +0000 Subject: [PATCH 13/62] Main functionality of File Watcher (Directory) CRL provider --- security/advancedtls/crl_provider.go | 146 ++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 709726472dd4..cc3200639b7f 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -20,8 +20,13 @@ package advancedtls import ( "crypto/x509" + "fmt" + "os" + "time" ) +const defaultCRLRefreshDuration = 1 * time.Hour + type CRLProvider interface { // Callers are expected to use the returned value as read-only. CRL(cert *x509.Certificate) (*CRL, error) @@ -41,10 +46,147 @@ func MakeStaticCRLProvider() *StaticCRLProvider { } func (p *StaticCRLProvider) AddCRL(crl *CRL) { - p.crls[crl.CertList.Issuer.ToRDNSequence().String()] = crl + key := crl.CertList.Issuer.ToRDNSequence().String() + p.crls[key] = crl } func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { // TODO handle no CRL found - return p.crls[cert.Issuer.ToRDNSequence().String()], nil + key := cert.Issuer.ToRDNSequence().String() + return p.crls[key], nil +} + +type Options struct { + CRLDirectory string + RefreshDuration time.Duration +} + +// NewFileWatcherCRLProvider creates a new FileWatcherCRLProvider. +type FileWatcherCRLProvider struct { + crls map[string]*CRL + opts Options + done chan bool +} + +func NewFileWatcherCRLProvider(o Options) (*FileWatcherCRLProvider, error) { + if err := o.validate(); err != nil { + return nil, err + } + return &FileWatcherCRLProvider{ + crls: make(map[string]*CRL), + opts: o, + done: make(chan bool), + }, nil +} + +func (o Options) validate() error { + // Checks relates to CRLDirectory. + if o.CRLDirectory == "" { + return fmt.Errorf("advancedtls: CRLDirectory needs to be specified") + } + fileInfo, err := os.Stat(o.CRLDirectory) + if err != nil { + if os.IsNotExist(err) { + return fmt.Errorf("advancedtls: CRLDirectory %v does not exist", o.CRLDirectory) + } else { + return err + } + } + if !fileInfo.IsDir() { + return fmt.Errorf("advancedtls: CRLDirectory %v is not a directory", o.CRLDirectory) + } + _, err = os.Open(o.CRLDirectory) + if err != nil { + if os.IsPermission(err) { + return fmt.Errorf("advancedtls: CRLDirectory %v is not readable:", o.CRLDirectory) + } else { + return err + } + } + // Checks related to RefreshDuration. + if o.RefreshDuration <= 0 || o.RefreshDuration < time.Second { + grpclogLogger.Warningf("RefreshDuration must larger then 1 second: provided value %v, default value will be used %v", o.RefreshDuration, defaultCRLRefreshDuration) + } + return nil +} + +// Start starts watching the directory for CRL files and updates the provider accordingly. +func (p *FileWatcherCRLProvider) Start() { + ticker := time.NewTicker(p.opts.RefreshDuration) + defer ticker.Stop() + + // Initial CRL load + p.scanCRLDirectory() + + for { + select { + case <-ticker.C: + p.scanCRLDirectory() + case <-p.done: + return + } + } +} + +// Stop stops the CRL provider and releases resources. +func (p *FileWatcherCRLProvider) Stop() { + close(p.done) +} + +func (p *FileWatcherCRLProvider) scanCRLDirectory() { + dir, err := os.Open(p.opts.CRLDirectory) + if err != nil { + grpclogLogger.Errorf("Can't open CRLDirectory %v", p.opts.CRLDirectory, err) + } + defer dir.Close() + + files, err := dir.ReadDir(0) + if err != nil { + grpclogLogger.Errorf("Can't access files under CRLDirectory %v", p.opts.CRLDirectory, err) + } + + successCounter := 0 + failCounter := 0 + for _, file := range files { + filePath := fmt.Sprintf("%s/%s", p.opts.CRLDirectory, file.Name()) + err := p.addCRL(filePath) + if err != nil { + failCounter++ + grpclogLogger.Warningf("Can't add CRL from file %v under CRLDirectory %v", filePath, p.opts.CRLDirectory, err) + continue + } + successCounter++ + } + grpclogLogger.Infof("Scan of CRLDirectory %v completed, tried %v files, added %v CRLs, %v files failed", len(files), successCounter, failCounter) +} + +func (p *FileWatcherCRLProvider) addCRL(filePath string) error { + crlBytes, err := os.ReadFile(filePath) + if err != nil { + return err + } + crl, err := parseRevocationList(crlBytes) + if err != nil { + return fmt.Errorf("addCRL: can't parse CRL from file %v: %v", filePath, err) + } + var certList *CRL + if certList, err = parseCRLExtensions(crl); err != nil { + return fmt.Errorf("addCRL: unsupported crl %v: %v", filePath, err) + } + rawCRLIssuer, err := extractCRLIssuer(crlBytes) + if err != nil { + return fmt.Errorf("addCRL: can't extract Issuer from CRL from file %v: %v", filePath, err) + } + certList.RawIssuer = rawCRLIssuer + key := certList.CertList.Issuer.ToRDNSequence().String() + p.crls[key] = certList + grpclogLogger.Infof("In-memory CRL storage of FileWatcherCRLProvider for key %v updated", key) + return nil +} + +// CRL retrieves the CRL associated with the given certificate's issuer DN. +func (p *FileWatcherCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { + // TODO handle no CRL found + key := cert.Issuer.ToRDNSequence().String() + return p.crls[key], nil } From a9a84f12518506c374474a9ef0eb2cd044d87454 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Fri, 29 Sep 2023 03:25:01 +0000 Subject: [PATCH 14/62] Refactor async go routine, validate() func, add unit tests --- security/advancedtls/crl_provider.go | 39 ++++---- security/advancedtls/crl_provider_test.go | 112 ++++++++++++++++++++++ 2 files changed, 133 insertions(+), 18 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index cc3200639b7f..319d23a7bc3c 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -19,6 +19,7 @@ package advancedtls import ( + "context" "crypto/x509" "fmt" "os" @@ -63,23 +64,26 @@ type Options struct { // NewFileWatcherCRLProvider creates a new FileWatcherCRLProvider. type FileWatcherCRLProvider struct { - crls map[string]*CRL - opts Options - done chan bool + crls map[string]*CRL + opts Options + cancel context.CancelFunc } -func NewFileWatcherCRLProvider(o Options) (*FileWatcherCRLProvider, error) { +func MakeFileWatcherCRLProvider(o Options) (*FileWatcherCRLProvider, error) { if err := o.validate(); err != nil { return nil, err } - return &FileWatcherCRLProvider{ + ctx, cancel := context.WithCancel(context.Background()) + provider := &FileWatcherCRLProvider{ crls: make(map[string]*CRL), opts: o, - done: make(chan bool), - }, nil + } + provider.cancel = cancel + go provider.run(ctx) + return provider, nil } -func (o Options) validate() error { +func (o *Options) validate() error { // Checks relates to CRLDirectory. if o.CRLDirectory == "" { return fmt.Errorf("advancedtls: CRLDirectory needs to be specified") @@ -105,32 +109,32 @@ func (o Options) validate() error { } // Checks related to RefreshDuration. if o.RefreshDuration <= 0 || o.RefreshDuration < time.Second { + o.RefreshDuration = defaultCRLRefreshDuration grpclogLogger.Warningf("RefreshDuration must larger then 1 second: provided value %v, default value will be used %v", o.RefreshDuration, defaultCRLRefreshDuration) } return nil } // Start starts watching the directory for CRL files and updates the provider accordingly. -func (p *FileWatcherCRLProvider) Start() { +func (p *FileWatcherCRLProvider) run(ctx context.Context) { ticker := time.NewTicker(p.opts.RefreshDuration) defer ticker.Stop() - - // Initial CRL load p.scanCRLDirectory() for { select { + case <-ctx.Done(): + ticker.Stop() + return case <-ticker.C: p.scanCRLDirectory() - case <-p.done: - return } } } // Stop stops the CRL provider and releases resources. -func (p *FileWatcherCRLProvider) Stop() { - close(p.done) +func (p *FileWatcherCRLProvider) Close() { + p.cancel() } func (p *FileWatcherCRLProvider) scanCRLDirectory() { @@ -157,7 +161,7 @@ func (p *FileWatcherCRLProvider) scanCRLDirectory() { } successCounter++ } - grpclogLogger.Infof("Scan of CRLDirectory %v completed, tried %v files, added %v CRLs, %v files failed", len(files), successCounter, failCounter) + grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files tried, %v CRLs added, %v files failed", len(files), successCounter, failCounter) } func (p *FileWatcherCRLProvider) addCRL(filePath string) error { @@ -171,7 +175,7 @@ func (p *FileWatcherCRLProvider) addCRL(filePath string) error { } var certList *CRL if certList, err = parseCRLExtensions(crl); err != nil { - return fmt.Errorf("addCRL: unsupported crl %v: %v", filePath, err) + return fmt.Errorf("addCRL: unsupported CRL %v: %v", filePath, err) } rawCRLIssuer, err := extractCRLIssuer(crlBytes) if err != nil { @@ -186,7 +190,6 @@ func (p *FileWatcherCRLProvider) addCRL(filePath string) error { // CRL retrieves the CRL associated with the given certificate's issuer DN. func (p *FileWatcherCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { - // TODO handle no CRL found key := cert.Issuer.ToRDNSequence().String() return p.crls[key], nil } diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index eacbb5a90271..cab4e81d3caf 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -22,6 +22,7 @@ import ( "crypto/x509" "fmt" "testing" + "time" "google.golang.org/grpc/security/advancedtls/testdata" ) @@ -71,3 +72,114 @@ func TestStaticCRLProvider(t *testing.T) { }) } } + +func TestFileWatcherCRLProviderConfig(t *testing.T) { + if _, err := MakeFileWatcherCRLProvider(Options{}); err == nil { + t.Fatalf("Empty Options should not be allowed") + } + if _, err := MakeFileWatcherCRLProvider(Options{CRLDirectory: "I_do_not_exist"}); err == nil { + t.Fatalf("CRLDirectory must exist") + } + if defaultProvider, err := MakeFileWatcherCRLProvider(Options{CRLDirectory: testdata.Path("crl/provider")}); err == nil { + if defaultProvider.opts.RefreshDuration != defaultCRLRefreshDuration { + t.Fatalf("RefreshDuration is not properly updated by validate() func") + } + defaultProvider.Close() + } else { + t.Fatal("Unexpected error:", err) + } + + regularProvider, err := MakeFileWatcherCRLProvider(Options{ + CRLDirectory: testdata.Path("crl"), + RefreshDuration: 5 * time.Second, + }) + if err != nil { + t.Fatal("Unexpected error while creating regular FileWatcherCRLProvider:", err) + } + + regularProvider.scanCRLDirectory() + tests := []struct { + desc string + certs []*x509.Certificate + expectNoCRL bool + }{ + { + desc: "Unrevoked chain", + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + }, + { + desc: "Revoked Intermediate chain", + certs: makeChain(t, testdata.Path("crl/revokedInt.pem")), + }, + { + desc: "Revoked leaf chain", + certs: makeChain(t, testdata.Path("crl/revokedLeaf.pem")), + }, + { + desc: "Chain with no CRL for issuer", + certs: makeChain(t, testdata.Path("client_cert_1.pem")), + expectNoCRL: true, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + for _, c := range tt.certs { + crl, err := regularProvider.CRL(c) + if err != nil { + t.Fatalf("Expected error fetch from provider: %v", err) + } + if crl == nil && !tt.expectNoCRL { + t.Fatalf("CRL is unexpectedly nil") + } + } + }) + } + regularProvider.Close() +} + +func TestFileWatcherCRLProvider(t *testing.T) { + p := MakeStaticCRLProvider() + for i := 1; i <= 6; i++ { + crl := loadCRL(t, testdata.Path(fmt.Sprintf("crl/%d.crl", i))) + p.AddCRL(crl) + } + + tests := []struct { + desc string + certs []*x509.Certificate + expectNoCRL bool + }{ + { + desc: "Unrevoked chain", + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + }, + { + desc: "Revoked Intermediate chain", + certs: makeChain(t, testdata.Path("crl/revokedInt.pem")), + }, + { + desc: "Revoked leaf chain", + certs: makeChain(t, testdata.Path("crl/revokedLeaf.pem")), + }, + { + desc: "Chain with no CRL for issuer", + certs: makeChain(t, testdata.Path("client_cert_1.pem")), + expectNoCRL: true, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + for _, c := range tt.certs { + crl, err := p.CRL(c) + if err != nil { + t.Fatalf("Expected error fetch from provider: %v", err) + } + if crl == nil && !tt.expectNoCRL { + t.Fatalf("CRL is unexpectedly nil") + } + } + }) + } +} From 5a0acad848b23576bbdc955476359885a042f6fa Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Fri, 29 Sep 2023 21:18:12 +0000 Subject: [PATCH 15/62] Custom error callback, related unit tests --- security/advancedtls/crl_provider.go | 14 ++++- security/advancedtls/crl_provider_test.go | 71 ++++++++--------------- 2 files changed, 37 insertions(+), 48 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 319d23a7bc3c..1bcf8b2a0017 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -58,8 +58,9 @@ func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { } type Options struct { - CRLDirectory string - RefreshDuration time.Duration + CRLDirectory string + RefreshDuration time.Duration + cRLReloadingFailedCallback func(err error) } // NewFileWatcherCRLProvider creates a new FileWatcherCRLProvider. @@ -141,12 +142,18 @@ func (p *FileWatcherCRLProvider) scanCRLDirectory() { dir, err := os.Open(p.opts.CRLDirectory) if err != nil { grpclogLogger.Errorf("Can't open CRLDirectory %v", p.opts.CRLDirectory, err) + if p.opts.cRLReloadingFailedCallback != nil { + p.opts.cRLReloadingFailedCallback(err) + } } defer dir.Close() files, err := dir.ReadDir(0) if err != nil { grpclogLogger.Errorf("Can't access files under CRLDirectory %v", p.opts.CRLDirectory, err) + if p.opts.cRLReloadingFailedCallback != nil { + p.opts.cRLReloadingFailedCallback(err) + } } successCounter := 0 @@ -157,6 +164,9 @@ func (p *FileWatcherCRLProvider) scanCRLDirectory() { if err != nil { failCounter++ grpclogLogger.Warningf("Can't add CRL from file %v under CRLDirectory %v", filePath, p.opts.CRLDirectory, err) + if p.opts.cRLReloadingFailedCallback != nil { + p.opts.cRLReloadingFailedCallback(err) + } continue } successCounter++ diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index cab4e81d3caf..84f918397ea4 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -27,6 +27,8 @@ import ( "google.golang.org/grpc/security/advancedtls/testdata" ) +const nonCRLFilesUnderCRLDirectory = 5 + func TestStaticCRLProvider(t *testing.T) { p := MakeStaticCRLProvider() for i := 1; i <= 6; i++ { @@ -89,62 +91,35 @@ func TestFileWatcherCRLProviderConfig(t *testing.T) { t.Fatal("Unexpected error:", err) } + customCallback := func(err error) { + fmt.Printf("Custom error message: %v", err) + } regularProvider, err := MakeFileWatcherCRLProvider(Options{ - CRLDirectory: testdata.Path("crl"), - RefreshDuration: 5 * time.Second, + CRLDirectory: testdata.Path("crl"), + RefreshDuration: 5 * time.Second, + cRLReloadingFailedCallback: customCallback, }) if err != nil { t.Fatal("Unexpected error while creating regular FileWatcherCRLProvider:", err) } - - regularProvider.scanCRLDirectory() - tests := []struct { - desc string - certs []*x509.Certificate - expectNoCRL bool - }{ - { - desc: "Unrevoked chain", - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - }, - { - desc: "Revoked Intermediate chain", - certs: makeChain(t, testdata.Path("crl/revokedInt.pem")), - }, - { - desc: "Revoked leaf chain", - certs: makeChain(t, testdata.Path("crl/revokedLeaf.pem")), - }, - { - desc: "Chain with no CRL for issuer", - certs: makeChain(t, testdata.Path("client_cert_1.pem")), - expectNoCRL: true, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - for _, c := range tt.certs { - crl, err := regularProvider.CRL(c) - if err != nil { - t.Fatalf("Expected error fetch from provider: %v", err) - } - if crl == nil && !tt.expectNoCRL { - t.Fatalf("CRL is unexpectedly nil") - } - } - }) - } regularProvider.Close() } func TestFileWatcherCRLProvider(t *testing.T) { - p := MakeStaticCRLProvider() - for i := 1; i <= 6; i++ { - crl := loadCRL(t, testdata.Path(fmt.Sprintf("crl/%d.crl", i))) - p.AddCRL(crl) + // testdata.Path("crl") contains 5 non-crl files. + failedCRlsCounter := 0 + customCallback := func(err error) { + failedCRlsCounter++ } - + p, err := MakeFileWatcherCRLProvider(Options{ + CRLDirectory: testdata.Path("crl"), + RefreshDuration: 5 * time.Second, + cRLReloadingFailedCallback: customCallback, + }) + if err != nil { + t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) + } + p.scanCRLDirectory() tests := []struct { desc string certs []*x509.Certificate @@ -182,4 +157,8 @@ func TestFileWatcherCRLProvider(t *testing.T) { } }) } + if failedCRlsCounter < nonCRLFilesUnderCRLDirectory { + t.Fatalf("Number of callback execution is smaller then number of non-CRL files: got %v, want at least %v", failedCRlsCounter, nonCRLFilesUnderCRLDirectory) + } + p.Close() } From f3c830b87881cd1bc2ba6d769fdd7cf647300d58 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Sun, 1 Oct 2023 03:32:41 +0000 Subject: [PATCH 16/62] Error callback test improvement --- security/advancedtls/crl_provider_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 84f918397ea4..ac3577c6a83f 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -107,9 +107,9 @@ func TestFileWatcherCRLProviderConfig(t *testing.T) { func TestFileWatcherCRLProvider(t *testing.T) { // testdata.Path("crl") contains 5 non-crl files. - failedCRlsCounter := 0 + nonCRLFilesSet := make(map[string]struct{}) customCallback := func(err error) { - failedCRlsCounter++ + nonCRLFilesSet[err.Error()] = struct{}{} } p, err := MakeFileWatcherCRLProvider(Options{ CRLDirectory: testdata.Path("crl"), @@ -157,8 +157,8 @@ func TestFileWatcherCRLProvider(t *testing.T) { } }) } - if failedCRlsCounter < nonCRLFilesUnderCRLDirectory { - t.Fatalf("Number of callback execution is smaller then number of non-CRL files: got %v, want at least %v", failedCRlsCounter, nonCRLFilesUnderCRLDirectory) + if len(nonCRLFilesSet) < nonCRLFilesUnderCRLDirectory { + t.Fatalf("Number of callback executions: got %v, want %v", len(nonCRLFilesSet), nonCRLFilesUnderCRLDirectory) } p.Close() } From 4ea1b3425328ffd1490a83879d32350a55e73cfe Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Sun, 1 Oct 2023 04:14:58 +0000 Subject: [PATCH 17/62] Comments for StaticCRLProvider --- security/advancedtls/crl_provider.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 1bcf8b2a0017..46f280ef5c8c 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -28,11 +28,25 @@ import ( const defaultCRLRefreshDuration = 1 * time.Hour +// CRLProvider is the interface to be implemented to enable custom CRL provider +// behavior. +// +// The interface defines how the data is read, but doesn't prescribe a way +// CRL are loaded and stored. Such implementations can be used in +// RevocationConfig of advancedtls.ClientOptions and/or +// advancedtls.ServerOptions . +// +// TODO(erm-g): Add link to related gRFC once it's ready. +// Please refer to https://github.com/grpc/proposal/ for more details. type CRLProvider interface { + // CRL accepts x509 Cert and returns back related CRL struct. The CRL struct + // can be nil, can contain empty or non-empty list of revkoed certificates. // Callers are expected to use the returned value as read-only. CRL(cert *x509.Certificate) (*CRL, error) } +// StaticCRLProvider implements CRLProvider interface by accepting CRL structs +// and storing them in-memory. type StaticCRLProvider struct { // TODO CRL is sort of our internal representation - provide an API for // people to read into it, or provide a simpler type in the API then @@ -40,19 +54,22 @@ type StaticCRLProvider struct { crls map[string]*CRL } +// MakeStaticCRLProvider returns a new instance of the StaticCRLProvider. func MakeStaticCRLProvider() *StaticCRLProvider { p := StaticCRLProvider{} p.crls = make(map[string]*CRL) return &p } +// AddCRL adds/updates provided CRL to in-memory storage. func (p *StaticCRLProvider) AddCRL(crl *CRL) { key := crl.CertList.Issuer.ToRDNSequence().String() p.crls[key] = crl } +// CRL returns CRL struct if it was previously loaded by calling AddCRL and +// found in-memory func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { - // TODO handle no CRL found key := cert.Issuer.ToRDNSequence().String() return p.crls[key], nil } From aeebd4ea3fb9ac1ec64bcad76f137b4e0bf2c04a Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 2 Oct 2023 00:49:09 +0000 Subject: [PATCH 18/62] Comments for public API --- security/advancedtls/crl_provider.go | 31 +++++++++++++++-------- security/advancedtls/crl_provider_test.go | 2 +- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 46f280ef5c8c..3a1cc12fa8ab 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -67,26 +67,31 @@ func (p *StaticCRLProvider) AddCRL(crl *CRL) { p.crls[key] = crl } -// CRL returns CRL struct if it was previously loaded by calling AddCRL and -// found in-memory +// CRL returns CRL struct if it was previously loaded by calling AddCRL. func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { key := cert.Issuer.ToRDNSequence().String() return p.crls[key], nil } +// Options represents a data structure holding a +// configuration for FileWatcherCRLProvider. type Options struct { - CRLDirectory string - RefreshDuration time.Duration - cRLReloadingFailedCallback func(err error) + CRLDirectory string // Path of the directory containing CRL files + RefreshDuration time.Duration // Time interval between CRLDirectory scans + cRLReloadingFailedCallback func(err error) // Custom callback executed when a CRL file can’t be processed } -// NewFileWatcherCRLProvider creates a new FileWatcherCRLProvider. +// FileWatcherCRLProvider implements the CRLProvider interface by periodically scanning +// CRLDirectory (see Options) and storing CRL structs in-memory type FileWatcherCRLProvider struct { crls map[string]*CRL opts Options cancel context.CancelFunc } +// MakeFileWatcherCRLProvider returns a new instance of the +// FileWatcherCRLProvider. It uses Options to validate and apply configuration +// required for creating a new instance. func MakeFileWatcherCRLProvider(o Options) (*FileWatcherCRLProvider, error) { if err := o.validate(); err != nil { return nil, err @@ -137,7 +142,7 @@ func (o *Options) validate() error { func (p *FileWatcherCRLProvider) run(ctx context.Context) { ticker := time.NewTicker(p.opts.RefreshDuration) defer ticker.Stop() - p.scanCRLDirectory() + p.ScanCRLDirectory() for { select { @@ -145,17 +150,20 @@ func (p *FileWatcherCRLProvider) run(ctx context.Context) { ticker.Stop() return case <-ticker.C: - p.scanCRLDirectory() + p.ScanCRLDirectory() } } } -// Stop stops the CRL provider and releases resources. +// Close stops the background refresh of CRLDirectory of FileWatcherCRLProvider func (p *FileWatcherCRLProvider) Close() { p.cancel() } -func (p *FileWatcherCRLProvider) scanCRLDirectory() { +// ScanCRLDirectory starts the process of scanning Options.CRLDirectory and +// updating in-memory storage of CRL structs.Please note that the same method is +// called periodically by run goroutine. +func (p *FileWatcherCRLProvider) ScanCRLDirectory() { dir, err := os.Open(p.opts.CRLDirectory) if err != nil { grpclogLogger.Errorf("Can't open CRLDirectory %v", p.opts.CRLDirectory, err) @@ -215,7 +223,8 @@ func (p *FileWatcherCRLProvider) addCRL(filePath string) error { return nil } -// CRL retrieves the CRL associated with the given certificate's issuer DN. +// CRL retrieves the CRL associated with the given certificate's issuer DN from +// in-memory if it was previously loaded during CRLDirectory scan. func (p *FileWatcherCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { key := cert.Issuer.ToRDNSequence().String() return p.crls[key], nil diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index ac3577c6a83f..dc10a940417c 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -119,7 +119,7 @@ func TestFileWatcherCRLProvider(t *testing.T) { if err != nil { t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) } - p.scanCRLDirectory() + p.ScanCRLDirectory() tests := []struct { desc string certs []*x509.Certificate From 735ac208228f638ea4526eae23c8d004476da96a Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 2 Oct 2023 03:49:06 +0000 Subject: [PATCH 19/62] go mod tidy --- security/advancedtls/go.sum | 1435 ----------------------------------- 1 file changed, 1435 deletions(-) diff --git a/security/advancedtls/go.sum b/security/advancedtls/go.sum index 99259aed2d05..2766a3d8223a 100644 --- a/security/advancedtls/go.sum +++ b/security/advancedtls/go.sum @@ -1,1457 +1,22 @@ -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= -cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= -cloud.google.com/go v0.110.0/go.mod h1:SJnCLqQ0FCFGSZMUNUf84MV3Aia54kn7pi8st7tMzaY= -cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= -cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= -cloud.google.com/go/accessapproval v1.6.0/go.mod h1:R0EiYnwV5fsRFiKZkPHr6mwyk2wxUJ30nL4j2pcFY2E= -cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= -cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= -cloud.google.com/go/accesscontextmanager v1.6.0/go.mod h1:8XCvZWfYw3K/ji0iVnp+6pu7huxoQTLmxAbVjbloTtM= -cloud.google.com/go/accesscontextmanager v1.7.0/go.mod h1:CEGLewx8dwa33aDAZQujl7Dx+uYhS0eay198wB/VumQ= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= -cloud.google.com/go/aiplatform v1.35.0/go.mod h1:7MFT/vCaOyZT/4IIFfxH4ErVg/4ku6lKv3w0+tFTgXQ= -cloud.google.com/go/aiplatform v1.36.1/go.mod h1:WTm12vJRPARNvJ+v6P52RDHCNe4AhvjcIZ/9/RRHy/k= -cloud.google.com/go/aiplatform v1.37.0/go.mod h1:IU2Cv29Lv9oCn/9LkFiiuKfwrRTq+QQMbW+hPCxJGZw= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/analytics v0.17.0/go.mod h1:WXFa3WSym4IZ+JiKmavYdJwGG/CvpqiqczmL59bTD9M= -cloud.google.com/go/analytics v0.18.0/go.mod h1:ZkeHGQlcIPkw0R/GW+boWHhCOR43xz9RN/jn7WcqfIE= -cloud.google.com/go/analytics v0.19.0/go.mod h1:k8liqf5/HCnOUkbawNtrWWc+UAzyDlW89doe8TtoDsE= -cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= -cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= -cloud.google.com/go/apigateway v1.5.0/go.mod h1:GpnZR3Q4rR7LVu5951qfXPJCHquZt02jf7xQx7kpqN8= -cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= -cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= -cloud.google.com/go/apigeeconnect v1.5.0/go.mod h1:KFaCqvBRU6idyhSNyn3vlHXc8VMDJdRmwDF6JyFRqZ8= -cloud.google.com/go/apigeeregistry v0.4.0/go.mod h1:EUG4PGcsZvxOXAdyEghIdXwAEi/4MEaoqLMLDMIwKXY= -cloud.google.com/go/apigeeregistry v0.5.0/go.mod h1:YR5+s0BVNZfVOUkMa5pAR2xGd0A473vA5M7j247o1wM= -cloud.google.com/go/apigeeregistry v0.6.0/go.mod h1:BFNzW7yQVLZ3yj0TKcwzb8n25CFBri51GVGOEUcgQsc= -cloud.google.com/go/apikeys v0.4.0/go.mod h1:XATS/yqZbaBK0HOssf+ALHp8jAlNHUgyfprvNcBIszU= -cloud.google.com/go/apikeys v0.5.0/go.mod h1:5aQfwY4D+ewMMWScd3hm2en3hCj+BROlyrt3ytS7KLI= -cloud.google.com/go/apikeys v0.6.0/go.mod h1:kbpXu5upyiAlGkKrJgQl8A0rKNNJ7dQ377pdroRSSi8= -cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= -cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= -cloud.google.com/go/appengine v1.6.0/go.mod h1:hg6i0J/BD2cKmDJbaFSYHFyZkgBEfQrDg/X0V5fJn84= -cloud.google.com/go/appengine v1.7.0/go.mod h1:eZqpbHFCqRGa2aCdope7eC0SWLV1j0neb/QnMJVWx6A= -cloud.google.com/go/appengine v1.7.1/go.mod h1:IHLToyb/3fKutRysUlFO0BPt5j7RiQ45nrzEJmKTo6E= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/area120 v0.7.0/go.mod h1:a3+8EUD1SX5RUcCs3MY5YasiO1z6yLiNLRiFrykbynY= -cloud.google.com/go/area120 v0.7.1/go.mod h1:j84i4E1RboTWjKtZVWXPqvK5VHQFJRF2c1Nm69pWm9k= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= -cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= -cloud.google.com/go/artifactregistry v1.11.1/go.mod h1:lLYghw+Itq9SONbCa1YWBoWs1nOucMH0pwXN1rOBZFI= -cloud.google.com/go/artifactregistry v1.11.2/go.mod h1:nLZns771ZGAwVLzTX/7Al6R9ehma4WUEhZGWV6CeQNQ= -cloud.google.com/go/artifactregistry v1.12.0/go.mod h1:o6P3MIvtzTOnmvGagO9v/rOjjA0HmhJ+/6KAXrmYDCI= -cloud.google.com/go/artifactregistry v1.13.0/go.mod h1:uy/LNfoOIivepGhooAUpL1i30Hgee3Cu0l4VTWHUC08= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= -cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= -cloud.google.com/go/asset v1.11.1/go.mod h1:fSwLhbRvC9p9CXQHJ3BgFeQNM4c9x10lqlrdEUYXlJo= -cloud.google.com/go/asset v1.12.0/go.mod h1:h9/sFOa4eDIyKmH6QMpm4eUK3pDojWnUhTgJlk762Hg= -cloud.google.com/go/asset v1.13.0/go.mod h1:WQAMyYek/b7NBpYq/K4KJWcRqzoalEsxz/t/dTk4THw= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= -cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= -cloud.google.com/go/assuredworkloads v1.10.0/go.mod h1:kwdUQuXcedVdsIaKgKTp9t0UJkE5+PAVNhdQm4ZVq2E= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= -cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= -cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= -cloud.google.com/go/automl v1.12.0/go.mod h1:tWDcHDp86aMIuHmyvjuKeeHEGq76lD7ZqfGLN6B0NuU= -cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= -cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= -cloud.google.com/go/baremetalsolution v0.5.0/go.mod h1:dXGxEkmR9BMwxhzBhV0AioD0ULBmuLZI8CdwalUxuss= -cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= -cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= -cloud.google.com/go/batch v0.7.0/go.mod h1:vLZN95s6teRUqRQ4s3RLDsH8PvboqBK+rn1oevL159g= -cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= -cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= -cloud.google.com/go/beyondcorp v0.4.0/go.mod h1:3ApA0mbhHx6YImmuubf5pyW8srKnCEPON32/5hj+RmM= -cloud.google.com/go/beyondcorp v0.5.0/go.mod h1:uFqj9X+dSfrheVp7ssLTaRHd2EHqSL4QZmH4e8WXGGU= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= -cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= -cloud.google.com/go/bigquery v1.47.0/go.mod h1:sA9XOgy0A8vQK9+MWhEQTY6Tix87M/ZurWFIxmF9I/E= -cloud.google.com/go/bigquery v1.48.0/go.mod h1:QAwSz+ipNgfL5jxiaK7weyOhzdoAy1zFm0Nf1fysJac= -cloud.google.com/go/bigquery v1.49.0/go.mod h1:Sv8hMmTFFYBlt/ftw2uN6dFdQPzBlREY9yBh7Oy7/4Q= -cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= -cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= -cloud.google.com/go/billing v1.12.0/go.mod h1:yKrZio/eu+okO/2McZEbch17O5CB5NpZhhXG6Z766ss= -cloud.google.com/go/billing v1.13.0/go.mod h1:7kB2W9Xf98hP9Sr12KfECgfGclsH3CQR0R08tnRlRbc= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= -cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= -cloud.google.com/go/binaryauthorization v1.5.0/go.mod h1:OSe4OU1nN/VswXKRBmciKpo9LulY41gch5c68htf3/Q= -cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= -cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= -cloud.google.com/go/certificatemanager v1.6.0/go.mod h1:3Hh64rCKjRAX8dXgRAyOcY5vQ/fE1sh8o+Mdd6KPgY8= -cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= -cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= -cloud.google.com/go/channel v1.11.0/go.mod h1:IdtI0uWGqhEeatSB62VOoJ8FSUhJ9/+iGkJVqp74CGE= -cloud.google.com/go/channel v1.12.0/go.mod h1:VkxCGKASi4Cq7TbXxlaBezonAYpp1GCnKMY6tnMQnLU= -cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= -cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= -cloud.google.com/go/cloudbuild v1.6.0/go.mod h1:UIbc/w9QCbH12xX+ezUsgblrWv+Cv4Tw83GiSMHOn9M= -cloud.google.com/go/cloudbuild v1.7.0/go.mod h1:zb5tWh2XI6lR9zQmsm1VRA+7OCuve5d8S+zJUul8KTg= -cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s= -cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= -cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= -cloud.google.com/go/clouddms v1.5.0/go.mod h1:QSxQnhikCLUw13iAbffF2CZxAER3xDGNHjsTAkQJcQA= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= -cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= -cloud.google.com/go/cloudtasks v1.9.0/go.mod h1:w+EyLsVkLWHcOaqNEyvcKAsWp9p29dL6uL9Nst1cI7Y= -cloud.google.com/go/cloudtasks v1.10.0/go.mod h1:NDSoTLkZ3+vExFEWu2UJV1arUyzVDAiZtdWcsUyNwBs= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= -cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= -cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOVXvgU0yacs= -cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= -cloud.google.com/go/compute v1.19.1/go.mod h1:6ylj3a05WF8leseCdIf77NK0g1ey+nj5IKd5/kvShxE= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= -cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= -cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= -cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= -cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= -cloud.google.com/go/container v1.13.1/go.mod h1:6wgbMPeQRw9rSnKBCAJXnds3Pzj03C4JHamr8asWKy4= -cloud.google.com/go/container v1.14.0/go.mod h1:3AoJMPhHfLDxLvrlVWaK57IXzaPnLaZq63WX59aQBfM= -cloud.google.com/go/container v1.15.0/go.mod h1:ft+9S0WGjAyjDggg5S06DXj+fHJICWg8L7isCQe9pQA= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/containeranalysis v0.7.0/go.mod h1:9aUL+/vZ55P2CXfuZjS4UjQ9AgXoSw8Ts6lemfmxBxI= -cloud.google.com/go/containeranalysis v0.9.0/go.mod h1:orbOANbwk5Ejoom+s+DUCTTJ7IBdBQJDcSylAx/on9s= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= -cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= -cloud.google.com/go/datacatalog v1.8.1/go.mod h1:RJ58z4rMp3gvETA465Vg+ag8BGgBdnRPEMMSTr5Uv+M= -cloud.google.com/go/datacatalog v1.12.0/go.mod h1:CWae8rFkfp6LzLumKOnmVh4+Zle4A3NXLzVJ1d1mRm0= -cloud.google.com/go/datacatalog v1.13.0/go.mod h1:E4Rj9a5ZtAxcQJlEBTLgMTphfP11/lNaAshpoBgemX8= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataflow v0.8.0/go.mod h1:Rcf5YgTKPtQyYz8bLYhFoIV/vP39eL7fWNcSOyFfLJE= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= -cloud.google.com/go/dataform v0.6.0/go.mod h1:QPflImQy33e29VuapFdf19oPbE4aYTJxr31OAPV+ulA= -cloud.google.com/go/dataform v0.7.0/go.mod h1:7NulqnVozfHvWUBpMDfKMUESr+85aJsC/2O0o3jWPDE= -cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= -cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= -cloud.google.com/go/datafusion v1.6.0/go.mod h1:WBsMF8F1RhSXvVM8rCV3AeyWVxcC2xY6vith3iw3S+8= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/datalabeling v0.7.0/go.mod h1:WPQb1y08RJbmpM3ww0CSUAGweL0SxByuW2E+FU+wXcM= -cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= -cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= -cloud.google.com/go/dataplex v1.5.2/go.mod h1:cVMgQHsmfRoI5KFYq4JtIBEUbYwc3c7tXmIDhRmNNVQ= -cloud.google.com/go/dataplex v1.6.0/go.mod h1:bMsomC/aEJOSpHXdFKFGQ1b0TDPIeL28nJObeO1ppRs= -cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= -cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= -cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= -cloud.google.com/go/dataqna v0.7.0/go.mod h1:Lx9OcIIeqCrw1a6KdO3/5KMP1wAmTc0slZWwP12Qq3c= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= -cloud.google.com/go/datastore v1.11.0/go.mod h1:TvGxBIHCS50u8jzG+AW/ppf87v1of8nwzFNgEZU1D3c= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= -cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= -cloud.google.com/go/datastream v1.6.0/go.mod h1:6LQSuswqLa7S4rPAOZFVjHIG3wJIjZcZrw8JDEDJuIs= -cloud.google.com/go/datastream v1.7.0/go.mod h1:uxVRMm2elUSPuh65IbZpzJNMbuzkcvu5CjMqVIUHrww= -cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= -cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= -cloud.google.com/go/deploy v1.6.0/go.mod h1:f9PTHehG/DjCom3QH0cntOVRm93uGBDt2vKzAPwpXQI= -cloud.google.com/go/deploy v1.8.0/go.mod h1:z3myEJnA/2wnB4sgjqdMfgxCA0EqC3RBTNcVPs93mtQ= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= -cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= -cloud.google.com/go/dialogflow v1.29.0/go.mod h1:b+2bzMe+k1s9V+F2jbJwpHPzrnIyHihAdRFMtn2WXuM= -cloud.google.com/go/dialogflow v1.31.0/go.mod h1:cuoUccuL1Z+HADhyIA7dci3N5zUssgpBJmCzI6fNRB4= -cloud.google.com/go/dialogflow v1.32.0/go.mod h1:jG9TRJl8CKrDhMEcvfcfFkkpp8ZhgPz3sBGmAUYJ2qE= -cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= -cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= -cloud.google.com/go/dlp v1.9.0/go.mod h1:qdgmqgTyReTz5/YNSSuueR8pl7hO0o9bQ39ZhtgkWp4= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= -cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= -cloud.google.com/go/documentai v1.16.0/go.mod h1:o0o0DLTEZ+YnJZ+J4wNfTxmDVyrkzFvttBXXtYRMHkM= -cloud.google.com/go/documentai v1.18.0/go.mod h1:F6CK6iUH8J81FehpskRmhLq/3VlwQvb7TvwOceQ2tbs= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/domains v0.8.0/go.mod h1:M9i3MMDzGFXsydri9/vW+EWz9sWb4I6WyHqdlAk0idE= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/edgecontainer v0.3.0/go.mod h1:FLDpP4nykgwwIfcLt6zInhprzw0lEi2P1fjO6Ie0qbc= -cloud.google.com/go/edgecontainer v1.0.0/go.mod h1:cttArqZpBB2q58W/upSG++ooo6EsblxDIolxa3jSjbY= -cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= -cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= -cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= -cloud.google.com/go/essentialcontacts v1.5.0/go.mod h1:ay29Z4zODTuwliK7SnX8E86aUF2CTzdNtvv42niCX0M= -cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= -cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= -cloud.google.com/go/eventarc v1.10.0/go.mod h1:u3R35tmZ9HvswGRBnF48IlYgYeBcPUCjkr4BTdem2Kw= -cloud.google.com/go/eventarc v1.11.0/go.mod h1:PyUjsUKPWoRBCHeOxZd/lbOOjahV41icXyUY5kSTvVY= -cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= -cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= -cloud.google.com/go/filestore v1.5.0/go.mod h1:FqBXDWBp4YLHqRnVGveOkHDf8svj9r5+mUDLupOWEDs= -cloud.google.com/go/filestore v1.6.0/go.mod h1:di5unNuss/qfZTw2U9nhFqo8/ZDSc466dre85Kydllg= -cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= -cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= -cloud.google.com/go/functions v1.10.0/go.mod h1:0D3hEOe3DbEvCXtYOZHQZmD+SzYsi1YbI7dGvHfldXw= -cloud.google.com/go/functions v1.12.0/go.mod h1:AXWGrF3e2C/5ehvwYo/GH6O5s09tOPksiKhz+hH8WkA= -cloud.google.com/go/functions v1.13.0/go.mod h1:EU4O007sQm6Ef/PwRsI8N2umygGqPBS/IZQKBQBcJ3c= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= -cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= -cloud.google.com/go/gaming v1.9.0/go.mod h1:Fc7kEmCObylSWLO334NcO+O9QMDyz+TKC4v1D7X+Bc0= -cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= -cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= -cloud.google.com/go/gkebackup v0.4.0/go.mod h1:byAyBGUwYGEEww7xsbnUTBHIYcOPy/PgUWUtOeRm9Vg= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkeconnect v0.7.0/go.mod h1:SNfmVqPkaEi3bF/B3CNZOAYPYdg7sU+obZ+QTky2Myw= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/gkehub v0.11.0/go.mod h1:JOWHlmN+GHyIbuWQPl47/C2RFhnFKH38jH9Ascu3n0E= -cloud.google.com/go/gkehub v0.12.0/go.mod h1:djiIwwzTTBrF5NaXCGv3mf7klpEMcST17VBTVVDcuaw= -cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= -cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= -cloud.google.com/go/gkemulticloud v0.5.0/go.mod h1:W0JDkiyi3Tqh0TJr//y19wyb1yf8llHVto2Htf2Ja3Y= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= -cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= -cloud.google.com/go/gsuiteaddons v1.5.0/go.mod h1:TFCClYLd64Eaa12sFVmUyG62tk4mdIsI7pAnSXRkcFo= -cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= -cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= -cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= -cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= -cloud.google.com/go/iam v0.11.0/go.mod h1:9PiLDanza5D+oWFZiH1uG+RnRCfEGKoyl6yo4cgWZGY= -cloud.google.com/go/iam v0.12.0/go.mod h1:knyHGviacl11zrtZUoDuYpDgLjvr28sLQaG0YB2GYAY= -cloud.google.com/go/iam v0.13.0/go.mod h1:ljOg+rcNfzZ5d6f1nAUJ8ZIxOaZUVoS14bKCtaLZ/D0= -cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= -cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= -cloud.google.com/go/iap v1.6.0/go.mod h1:NSuvI9C/j7UdjGjIde7t7HBz+QTwBcapPE07+sSRcLk= -cloud.google.com/go/iap v1.7.0/go.mod h1:beqQx56T9O1G1yNPph+spKpNibDlYIiIixiqsQXxLIo= -cloud.google.com/go/iap v1.7.1/go.mod h1:WapEwPc7ZxGt2jFGB/C/bm+hP0Y6NXzOYGjpPnmMS74= -cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= -cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= -cloud.google.com/go/ids v1.3.0/go.mod h1:JBdTYwANikFKaDP6LtW5JAi4gubs57SVNQjemdt6xV4= -cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= -cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= -cloud.google.com/go/iot v1.5.0/go.mod h1:mpz5259PDl3XJthEmh9+ap0affn/MqNSP4My77Qql9o= -cloud.google.com/go/iot v1.6.0/go.mod h1:IqdAsmE2cTYYNO1Fvjfzo9po179rAtJeVGUvkLN3rLE= -cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= -cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= -cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= -cloud.google.com/go/kms v1.8.0/go.mod h1:4xFEhYFqvW+4VMELtZyxomGSYtSQKzM178ylFW4jMAg= -cloud.google.com/go/kms v1.9.0/go.mod h1:qb1tPTgfF9RQP8e1wq4cLFErVuTJv7UsSC915J8dh3w= -cloud.google.com/go/kms v1.10.0/go.mod h1:ng3KTUtQQU9bPX3+QGLsflZIHlkbn8amFAMY63m8d24= -cloud.google.com/go/kms v1.10.1/go.mod h1:rIWk/TryCkR59GMC3YtHtXeLzd634lBbKenvyySAyYI= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= -cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= -cloud.google.com/go/language v1.9.0/go.mod h1:Ns15WooPM5Ad/5no/0n81yUetis74g3zrbeJBE+ptUY= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo= -cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= -cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M= -cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= -cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= -cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= -cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= -cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= -cloud.google.com/go/managedidentities v1.5.0/go.mod h1:+dWcZ0JlUmpuxpIDfyP5pP5y0bLdRwOS4Lp7gMni/LA= -cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= -cloud.google.com/go/maps v0.6.0/go.mod h1:o6DAMMfb+aINHz/p/jbcY+mYeXBoZoxTfdSQ8VAJaCw= -cloud.google.com/go/maps v0.7.0/go.mod h1:3GnvVl3cqeSvgMcpRlQidXsPYuDGQ8naBis7MVzpXsY= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/mediatranslation v0.7.0/go.mod h1:LCnB/gZr90ONOIQLgSXagp8XUW1ODs2UmUMvcgMfI2I= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= -cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= -cloud.google.com/go/memcache v1.9.0/go.mod h1:8oEyzXCu+zo9RzlEaEjHl4KkgjlNDaXbCQeQWlzNFJM= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= -cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= -cloud.google.com/go/metastore v1.10.0/go.mod h1:fPEnH3g4JJAk+gMRnrAnoqyv2lpUCqJPWOodSaf45Eo= -cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= -cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= -cloud.google.com/go/monitoring v1.12.0/go.mod h1:yx8Jj2fZNEkL/GYZyTLS4ZtZEZN8WtDEiEqG4kLK50w= -cloud.google.com/go/monitoring v1.13.0/go.mod h1:k2yMBAB1H9JT/QETjNkgdCGD9bPF712XiLTVr+cBrpw= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= -cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= -cloud.google.com/go/networkconnectivity v1.10.0/go.mod h1:UP4O4sWXJG13AqrTdQCD9TnLGEbtNRqjuaaA7bNjF5E= -cloud.google.com/go/networkconnectivity v1.11.0/go.mod h1:iWmDD4QF16VCDLXUqvyspJjIEtBR/4zq5hwnY2X3scM= -cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= -cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= -cloud.google.com/go/networkmanagement v1.6.0/go.mod h1:5pKPqyXjB/sgtvB5xqOemumoQNB7y95Q7S+4rjSOPYY= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/networksecurity v0.7.0/go.mod h1:mAnzoxx/8TBSyXEeESMy9OOYwo1v+gZ5eMRnsT5bC8k= -cloud.google.com/go/networksecurity v0.8.0/go.mod h1:B78DkqsxFG5zRSVuwYFRZ9Xz8IcQ5iECsNrPn74hKHU= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= -cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= -cloud.google.com/go/notebooks v1.7.0/go.mod h1:PVlaDGfJgj1fl1S3dUwhFMXFgfYGhYQt2164xOMONmE= -cloud.google.com/go/notebooks v1.8.0/go.mod h1:Lq6dYKOYOWUCTvw5t2q1gp1lAp0zxAxRycayS0iJcqQ= -cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= -cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= -cloud.google.com/go/optimization v1.3.1/go.mod h1:IvUSefKiwd1a5p0RgHDbWCIbDFgKuEdB+fPPuP0IDLI= -cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= -cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= -cloud.google.com/go/orchestration v1.6.0/go.mod h1:M62Bevp7pkxStDfFfTuCOaXgaaqRAga1yKyoMtEoWPQ= -cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= -cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= -cloud.google.com/go/orgpolicy v1.10.0/go.mod h1:w1fo8b7rRqlXlIJbVhOMPrwVljyuW5mqssvBtU18ONc= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= -cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= -cloud.google.com/go/osconfig v1.11.0/go.mod h1:aDICxrur2ogRd9zY5ytBLV89KEgT2MKB2L/n6x1ooPw= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= -cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= -cloud.google.com/go/oslogin v1.9.0/go.mod h1:HNavntnH8nzrn8JCTT5fj18FuJLFJc4NaZJtBnQtKFs= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/phishingprotection v0.7.0/go.mod h1:8qJI4QKHoda/sb/7/YmMQ2omRLSLYSu9bU0EKCNI+Lk= -cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= -cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= -cloud.google.com/go/policytroubleshooter v1.5.0/go.mod h1:Rz1WfV+1oIpPdN2VvvuboLVRsB1Hclg3CKQ53j9l8vw= -cloud.google.com/go/policytroubleshooter v1.6.0/go.mod h1:zYqaPTsmfvpjm5ULxAyD/lINQxJ0DDsnWOP/GZ7xzBc= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= -cloud.google.com/go/privatecatalog v0.7.0/go.mod h1:2s5ssIFO69F5csTXcwBP7NPFTZvps26xGzvQ2PQaBYg= -cloud.google.com/go/privatecatalog v0.8.0/go.mod h1:nQ6pfaegeDAq/Q5lrfCQzQLhubPiZhSaNhIgfJlnIXs= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= -cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= -cloud.google.com/go/pubsub v1.28.0/go.mod h1:vuXFpwaVoIPQMGXqRyUQigu/AX1S3IWugR9xznmcXX8= -cloud.google.com/go/pubsub v1.30.0/go.mod h1:qWi1OPS0B+b5L+Sg6Gmc9zD1Y+HaM0MdUr7LsupY1P4= -cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= -cloud.google.com/go/pubsublite v1.6.0/go.mod h1:1eFCS0U11xlOuMFV/0iBqw3zP12kddMeCbj/F3FSj9k= -cloud.google.com/go/pubsublite v1.7.0/go.mod h1:8hVMwRXfDfvGm3fahVbtDbiLePT3gpoiJYJY+vxWxVM= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= -cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= -cloud.google.com/go/recaptchaenterprise/v2 v2.6.0/go.mod h1:RPauz9jeLtB3JVzg6nCbe12qNoaa8pXc4d/YukAmcnA= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0/go.mod h1:19wVj/fs5RtYtynAPJdDTb69oW0vNHYDBTbB4NvMD9c= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommendationengine v0.7.0/go.mod h1:1reUcE3GIu6MeBz/h5xZJqNLuuVjNg1lmWMPyjatzac= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= -cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= -cloud.google.com/go/recommender v1.9.0/go.mod h1:PnSsnZY7q+VL1uax2JWkt/UegHssxjUVVCrX52CuEmQ= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= -cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= -cloud.google.com/go/redis v1.11.0/go.mod h1:/X6eicana+BWcUda5PpwZC48o37SiFVTFSs0fWAJ7uQ= -cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= -cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= -cloud.google.com/go/resourcemanager v1.5.0/go.mod h1:eQoXNAiAvCf5PXxWxXjhKQoTMaUSNrEfg+6qdf/wots= -cloud.google.com/go/resourcemanager v1.6.0/go.mod h1:YcpXGRs8fDzcUl1Xw8uOVmI8JEadvhRIkoXXUNVYcVo= -cloud.google.com/go/resourcemanager v1.7.0/go.mod h1:HlD3m6+bwhzj9XCouqmeiGuni95NTrExfhoSrkC/3EI= -cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= -cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= -cloud.google.com/go/resourcesettings v1.5.0/go.mod h1:+xJF7QSG6undsQDfsCJyqWXyBwUoJLhetkRMDRnIoXA= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= -cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= -cloud.google.com/go/retail v1.12.0/go.mod h1:UMkelN/0Z8XvKymXFbD4EhFJlYKRx1FGhQkVPU5kF14= -cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= -cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= -cloud.google.com/go/run v0.8.0/go.mod h1:VniEnuBwqjigv0A7ONfQUaEItaiCRVujlMqerPPiktM= -cloud.google.com/go/run v0.9.0/go.mod h1:Wwu+/vvg8Y+JUApMwEDfVfhetv30hCG4ZwDR/IXl2Qg= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= -cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= -cloud.google.com/go/scheduler v1.8.0/go.mod h1:TCET+Y5Gp1YgHT8py4nlg2Sew8nUHMqcpousDgXJVQc= -cloud.google.com/go/scheduler v1.9.0/go.mod h1:yexg5t+KSmqu+njTIh3b7oYPheFtBWGcbVUYF1GGMIc= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= -cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= -cloud.google.com/go/secretmanager v1.10.0/go.mod h1:MfnrdvKMPNra9aZtQFvBcvRU54hbPD8/HayQdlUgJpU= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= -cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= -cloud.google.com/go/security v1.12.0/go.mod h1:rV6EhrpbNHrrxqlvW0BWAIawFWq3X90SduMJdFwtLB8= -cloud.google.com/go/security v1.13.0/go.mod h1:Q1Nvxl1PAgmeW0y3HTt54JYIvUdtcpYKVfIB8AOMZ+0= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= -cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= -cloud.google.com/go/securitycenter v1.18.1/go.mod h1:0/25gAzCM/9OL9vVx4ChPeM/+DlfGQJDwBy/UC8AKK0= -cloud.google.com/go/securitycenter v1.19.0/go.mod h1:LVLmSg8ZkkyaNy4u7HCIshAngSQ8EcIRREP3xBnyfag= -cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= -cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= -cloud.google.com/go/servicecontrol v1.10.0/go.mod h1:pQvyvSRh7YzUF2efw7H87V92mxU8FnFDawMClGCNuAA= -cloud.google.com/go/servicecontrol v1.11.0/go.mod h1:kFmTzYzTUIuZs0ycVqRHNaNhgR+UMUpw9n02l/pY+mc= -cloud.google.com/go/servicecontrol v1.11.1/go.mod h1:aSnNNlwEFBY+PWGQ2DoM0JJ/QUXqV5/ZD9DOLB7SnUk= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= -cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= -cloud.google.com/go/servicedirectory v1.8.0/go.mod h1:srXodfhY1GFIPvltunswqXpVxFPpZjf8nkKQT7XcXaY= -cloud.google.com/go/servicedirectory v1.9.0/go.mod h1:29je5JjiygNYlmsGz8k6o+OZ8vd4f//bQLtvzkPPT/s= -cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= -cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= -cloud.google.com/go/servicemanagement v1.6.0/go.mod h1:aWns7EeeCOtGEX4OvZUWCCJONRZeFKiptqKf1D0l/Jc= -cloud.google.com/go/servicemanagement v1.8.0/go.mod h1:MSS2TDlIEQD/fzsSGfCdJItQveu9NXnUniTrq/L8LK4= -cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= -cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= -cloud.google.com/go/serviceusage v1.5.0/go.mod h1:w8U1JvqUqwJNPEOTQjrMHkw3IaIFLoLsPLvsE3xueec= -cloud.google.com/go/serviceusage v1.6.0/go.mod h1:R5wwQcbOWsyuOfbP9tGdAnCAc6B9DRwPG1xtWMDeuPA= -cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= -cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= -cloud.google.com/go/shell v1.6.0/go.mod h1:oHO8QACS90luWgxP3N9iZVuEiSF84zNyLytb+qE2f9A= -cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= -cloud.google.com/go/spanner v1.44.0/go.mod h1:G8XIgYdOK+Fbcpbs7p2fiprDw4CaZX63whnSMLVBxjk= -cloud.google.com/go/spanner v1.45.0/go.mod h1:FIws5LowYz8YAE1J8fOS7DJup8ff7xJeetWEo5REA2M= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= -cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= -cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= -cloud.google.com/go/speech v1.14.1/go.mod h1:gEosVRPJ9waG7zqqnsHpYTOoAS4KouMRLDFMekpJ0J0= -cloud.google.com/go/speech v1.15.0/go.mod h1:y6oH7GhqCaZANH7+Oe0BhgIogsNInLlz542tg3VqeYI= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.28.1/go.mod h1:Qnisd4CqDdo6BGs2AD5LLnEsmSQ80wQ5ogcBBKhU86Y= -cloud.google.com/go/storage v1.29.0/go.mod h1:4puEjyTKnku6gfKoTfNOU/W+a9JyuVNxjpS5GBrB8h4= -cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= -cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= -cloud.google.com/go/storagetransfer v1.7.0/go.mod h1:8Giuj1QNb1kfLAiWM1bN6dHzfdlDAVC9rv9abHot2W4= -cloud.google.com/go/storagetransfer v1.8.0/go.mod h1:JpegsHHU1eXg7lMHkvf+KE5XDJ7EQu0GwNJbbVGanEw= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= -cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= -cloud.google.com/go/talent v1.5.0/go.mod h1:G+ODMj9bsasAEJkQSzO2uHQWXHHXUomArjWQQYkqK6c= -cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= -cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= -cloud.google.com/go/texttospeech v1.6.0/go.mod h1:YmwmFT8pj1aBblQOI3TfKmwibnsfvhIBzPXcW4EBovc= -cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= -cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= -cloud.google.com/go/tpu v1.5.0/go.mod h1:8zVo1rYDFuW2l4yZVY0R0fb/v44xLh3llq7RuV61fPM= -cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= -cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= -cloud.google.com/go/trace v1.8.0/go.mod h1:zH7vcsbAhklH8hWFig58HvxcxyQbaIqMarMg9hn5ECA= -cloud.google.com/go/trace v1.9.0/go.mod h1:lOQqpE5IaWY0Ixg7/r2SjixMuc6lfTFeO4QGM4dQWOk= -cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= -cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= -cloud.google.com/go/translate v1.5.0/go.mod h1:29YDSYveqqpA1CQFD7NQuP49xymq17RXNaUDdc0mNu0= -cloud.google.com/go/translate v1.6.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/translate v1.7.0/go.mod h1:lMGRudH1pu7I3n3PETiOB2507gf3HnfLV8qlkHZEyos= -cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= -cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= -cloud.google.com/go/video v1.12.0/go.mod h1:MLQew95eTuaNDEGriQdcYn0dTwf9oWiA4uYebxM5kdg= -cloud.google.com/go/video v1.13.0/go.mod h1:ulzkYlYgCp15N2AokzKjy7MQ9ejuynOJdf1tR5lGthk= -cloud.google.com/go/video v1.14.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/video v1.15.0/go.mod h1:SkgaXwT+lIIAKqWAJfktHT/RbgjSuY6DobxEp0C5yTQ= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= -cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= -cloud.google.com/go/videointelligence v1.10.0/go.mod h1:LHZngX1liVtUhZvi2uNS0VQuOzNi2TkY1OakiuoUOjU= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= -cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= -cloud.google.com/go/vision/v2 v2.6.0/go.mod h1:158Hes0MvOS9Z/bDMSFpjwsUrZ5fPrdwuyyvKSGAGMY= -cloud.google.com/go/vision/v2 v2.7.0/go.mod h1:H89VysHy21avemp6xcf9b9JvZHVehWbET0uT/bcuY/0= -cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= -cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= -cloud.google.com/go/vmmigration v1.5.0/go.mod h1:E4YQ8q7/4W9gobHjQg4JJSgXXSgY21nA5r8swQV+Xxc= -cloud.google.com/go/vmmigration v1.6.0/go.mod h1:bopQ/g4z+8qXzichC7GW1w2MjbErL54rk3/C843CjfY= -cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= -cloud.google.com/go/vmwareengine v0.2.2/go.mod h1:sKdctNJxb3KLZkE/6Oui94iw/xs9PRNC2wnNLXsHvH8= -cloud.google.com/go/vmwareengine v0.3.0/go.mod h1:wvoyMvNWdIzxMYSpH/R7y2h5h3WFkx6d+1TIsP39WGY= -cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= -cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= -cloud.google.com/go/vpcaccess v1.6.0/go.mod h1:wX2ILaNhe7TlVa4vC5xce1bCnqE3AeH27RV31lnmZes= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= -cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= -cloud.google.com/go/webrisk v1.8.0/go.mod h1:oJPDuamzHXgUc+b8SiHRcVInZQuybnvEW72PqTc7sSg= -cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= -cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= -cloud.google.com/go/websecurityscanner v1.5.0/go.mod h1:Y6xdCPy81yi0SQnDY1xdNTNpfY1oAgXUlcfN3B3eSng= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= -cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= -cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= -cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= -git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= -github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= -github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= -github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= -github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= -github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= -github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= -github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= -github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= -github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= -github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= -github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/flatbuffers v2.0.8+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= -github.com/googleapis/enterprise-certificate-proxy v0.2.1/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/enterprise-certificate-proxy v0.2.3/go.mod h1:AwSRAtLfXpU5Nm3pW+v7rGDHp09LsPtGY9MduiEsR9k= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= -github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= -github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= -github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= -github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= -github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= -golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220829200755-d48e67d00261/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= -gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= -gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= -gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= -gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= -google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= -google.golang.org/api v0.106.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.107.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.108.0/go.mod h1:2Ts0XTHNVWxypznxWOYUeI4g3WdP9Pk2Qk58+a/O9MY= -google.golang.org/api v0.110.0/go.mod h1:7FC4Vvx1Mooxh8C5HWjzZHcavuS2f6pmJpZx60ca7iI= -google.golang.org/api v0.111.0/go.mod h1:qtFHvU9mhgTJegR31csQ+rwxyUTHOKFqCKWp1J0fdw0= -google.golang.org/api v0.114.0/go.mod h1:ifYI2ZsFK6/uGddGfAD5BMxlnkBqCmqHSDUVi45N5Yg= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= -google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= -google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= -google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230112194545-e10362b5ecf9/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230113154510-dbe35b8444a5/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230123190316-2c411cf9d197/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230124163310-31e0e69b6fc2/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230125152338-dcaf20b6aeaa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230127162408-596548ed4efa/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= -google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA= -google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw= -google.golang.org/genproto v0.0.0-20230303212802-e74f57abe488/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230320184635-7606e756e683/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s= -google.golang.org/genproto v0.0.0-20230323212658-478b75c54725/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230330154414-c0448cd141ea/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230331144136-dcfb400f0633/go.mod h1:UUQDJDOlWu4KYeJZffbWgBkS1YFobzKbLVfK69pe0Ak= -google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54/go.mod h1:zqTuNwFlFRsw5zIts5VnzLQxSRqh+CGOTVMlYbY0Eyk= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8= -google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= -modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= -modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= -modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= -modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= -modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= -modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= -modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU= -modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= -modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= -modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= -modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= -modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= -modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= -modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw= -modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From 5c76a603c4da671b834b3fbc8518e35c91f1a7e4 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 2 Oct 2023 04:27:36 +0000 Subject: [PATCH 20/62] Comments for tests --- security/advancedtls/crl_provider_test.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index dc10a940417c..b957a4da84b2 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -29,13 +29,18 @@ import ( const nonCRLFilesUnderCRLDirectory = 5 +// TestStaticCRLProvider tests how StaticCRLProvider handles the major four +// cases for CRL checks. It loads the CRLs under crl directory, constructs +// unrevoked, revoked leaf, and revoked intermediate chains, as well as a chain +// without CRL for issuer, and checks that it’s correctly processed. func TestStaticCRLProvider(t *testing.T) { p := MakeStaticCRLProvider() for i := 1; i <= 6; i++ { crl := loadCRL(t, testdata.Path(fmt.Sprintf("crl/%d.crl", i))) p.AddCRL(crl) } - + // Each test data entry contains a description of a certificate chain, + // certificate chain itself, and if CRL is not expected to be found. tests := []struct { desc string certs []*x509.Certificate @@ -75,6 +80,9 @@ func TestStaticCRLProvider(t *testing.T) { } } +// TestFileWatcherCRLProviderConfig checks creation of FileWatcherCRLProvider, +// and the validation of Options configuration. The configurations include empty +// one, non existing CRLDirectory, invalid RefreshDuration, and the correct one. func TestFileWatcherCRLProviderConfig(t *testing.T) { if _, err := MakeFileWatcherCRLProvider(Options{}); err == nil { t.Fatalf("Empty Options should not be allowed") @@ -105,6 +113,12 @@ func TestFileWatcherCRLProviderConfig(t *testing.T) { regularProvider.Close() } +// TestFileWatcherCRLProvider tests how FileWatcherCRLProvider handles the major +// four cases for CRL checks. It scans the CRLs under crl directory to populate +// the in-memory storage. Then we construct unrevoked, revoked leaf, and revoked +// intermediate chains, as well as a chain without CRL for issuer, and check +// that it’s correctly processed. Additionally, we also check if number of +// invocations of custom callback is correct. func TestFileWatcherCRLProvider(t *testing.T) { // testdata.Path("crl") contains 5 non-crl files. nonCRLFilesSet := make(map[string]struct{}) @@ -120,6 +134,9 @@ func TestFileWatcherCRLProvider(t *testing.T) { t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) } p.ScanCRLDirectory() + + // Each test data entry contains a description of a certificate chain, + // certificate chain itself, and if CRL is not expected to be found. tests := []struct { desc string certs []*x509.Certificate From 0bc7757f35fd116567335cbdf63a1eb754f08982 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 2 Oct 2023 12:25:41 +0000 Subject: [PATCH 21/62] Fix vet errors --- security/advancedtls/crl.go | 6 ++++-- security/advancedtls/crl_provider.go | 8 +++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index dcc8dae738f9..8153f1f2f340 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -99,6 +99,7 @@ type CRL struct { RawIssuer []byte } +// NewCRL constructs new CRL from provided byte array. func NewCRL(b []byte) (*CRL, error) { crl, err := parseRevocationList(b) if err != nil { @@ -115,6 +116,8 @@ func NewCRL(b []byte) (*CRL, error) { return crlExt, nil } +// ReadCRLFile reads a file from the provided path, and returns constructed +// from it. func ReadCRLFile(path string) (*CRL, error) { b, err := os.ReadFile(path) if err != nil { @@ -299,9 +302,8 @@ func fetchCRL(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg Revocat return nil, fmt.Errorf("no CRL found for certificate's issuer") } return crl, nil - } else { - return fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) } + return fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) } // checkCert checks a single certificate against the CRL defined in the certificate. diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 3a1cc12fa8ab..67c1c459064e 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -115,9 +115,8 @@ func (o *Options) validate() error { if err != nil { if os.IsNotExist(err) { return fmt.Errorf("advancedtls: CRLDirectory %v does not exist", o.CRLDirectory) - } else { - return err } + return err } if !fileInfo.IsDir() { return fmt.Errorf("advancedtls: CRLDirectory %v is not a directory", o.CRLDirectory) @@ -125,10 +124,9 @@ func (o *Options) validate() error { _, err = os.Open(o.CRLDirectory) if err != nil { if os.IsPermission(err) { - return fmt.Errorf("advancedtls: CRLDirectory %v is not readable:", o.CRLDirectory) - } else { - return err + return fmt.Errorf("advancedtls: CRLDirectory %v is not readable", o.CRLDirectory) } + return err } // Checks related to RefreshDuration. if o.RefreshDuration <= 0 || o.RefreshDuration < time.Second { From f844c8cd889638d1388947780ac9786e65bf55e6 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 3 Oct 2023 14:07:35 +0000 Subject: [PATCH 22/62] Change Static provider behavior to match C Core, address other PR comments --- security/advancedtls/advancedtls_test.go | 28 ++++++++++++++--------- security/advancedtls/crl_provider.go | 19 +++++++++++---- security/advancedtls/crl_provider_test.go | 11 ++++++--- security/advancedtls/crl_test.go | 10 +++++--- 4 files changed, 46 insertions(+), 22 deletions(-) diff --git a/security/advancedtls/advancedtls_test.go b/security/advancedtls/advancedtls_test.go index da5ac2c783a8..c0b4123d5121 100644 --- a/security/advancedtls/advancedtls_test.go +++ b/security/advancedtls/advancedtls_test.go @@ -25,6 +25,7 @@ import ( "errors" "fmt" "net" + "os" "testing" lru "github.com/hashicorp/golang-lru" @@ -381,15 +382,20 @@ func (s) TestClientServerHandshake(t *testing.T) { } makeStaticCRLProvider := func(containsRevoked bool) *RevocationConfig { - cRLProvider := MakeStaticCRLProvider() - var crl *CRL + + rawCRLs := make([][]byte, 1) + var path string if containsRevoked { - crl = loadCRL(t, testdata.Path("crl/provider/crl_server_revoked.pem")) + path = testdata.Path("crl/provider/crl_server_revoked.pem") } else { - crl = loadCRL(t, testdata.Path("crl/provider/crl_empty.pem")) + path = testdata.Path("crl/provider/crl_empty.pem") } - cRLProvider.AddCRL(crl) - + rawCRL, err := os.ReadFile(path) + if err != nil { + t.Fatalf("readFile(%v) failed err = %v", path, err) + } + rawCRLs = append(rawCRLs, rawCRL) + cRLProvider := MakeStaticCRLProvider(rawCRLs) return &RevocationConfig{ AllowUndetermined: true, CRLProvider: cRLProvider, @@ -619,14 +625,14 @@ func (s) TestClientServerHandshake(t *testing.T) { // server custom check fails { desc: "Client sets peer cert, reload root function with verifyFuncGood; Server sets bad custom check; mutualTLS", - clientCert: []tls.Certificate{cs.ClientCert3}, - clientGetRoot: getRootCAsForClientCRL, + clientCert: []tls.Certificate{cs.ClientCert1}, + clientGetRoot: getRootCAsForClient, clientVerifyFunc: clientVerifyFuncGood, clientVType: CertVerification, clientExpectHandshakeError: true, serverMutualTLS: true, - serverCert: []tls.Certificate{cs.ServerCert3}, - serverGetRoot: getRootCAsForServerCRL, + serverCert: []tls.Certificate{cs.ServerCert1}, + serverGetRoot: getRootCAsForServer, serverVerifyFunc: verifyFuncBad, serverVType: CertVerification, serverExpectError: true, @@ -707,7 +713,7 @@ func (s) TestClientServerHandshake(t *testing.T) { }, // Client: set valid credentials with the revocation config // Server: set valid credentials with the revocation config - // Expected Behavior: success, because non of the certificate chains sent in the connection are revoked + // Expected Behavior: success, because none of the certificate chains sent in the connection are revoked { desc: "Client sets peer cert, reload root function with verifyFuncGood; Server sets peer cert, reload root function; mutualTLS", clientCert: []tls.Certificate{cs.ClientCert1}, diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 67c1c459064e..103ff8b11af9 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -45,8 +45,8 @@ type CRLProvider interface { CRL(cert *x509.Certificate) (*CRL, error) } -// StaticCRLProvider implements CRLProvider interface by accepting CRL structs -// and storing them in-memory. +// StaticCRLProvider implements CRLProvider interface by accepting raw content +// of CRL files at creation time and storing parsed CRL structs in-memory. type StaticCRLProvider struct { // TODO CRL is sort of our internal representation - provide an API for // people to read into it, or provide a simpler type in the API then @@ -54,15 +54,24 @@ type StaticCRLProvider struct { crls map[string]*CRL } -// MakeStaticCRLProvider returns a new instance of the StaticCRLProvider. -func MakeStaticCRLProvider() *StaticCRLProvider { +// MakeStaticCRLProvider processes raw content of CRL files, adds parsed CRL +// structs into in-memory, and returns a new instance of the StaticCRLProvider. +func MakeStaticCRLProvider(rawCRLs [][]byte) *StaticCRLProvider { p := StaticCRLProvider{} p.crls = make(map[string]*CRL) + for idx, rawCRL := range rawCRLs { + cRL, err := NewCRL(rawCRL) + if err != nil { + grpclogLogger.Warningf("Can't parse raw CRL number %v from the slice: %v", idx, err) + continue + } + p.addCRL(cRL) + } return &p } // AddCRL adds/updates provided CRL to in-memory storage. -func (p *StaticCRLProvider) AddCRL(crl *CRL) { +func (p *StaticCRLProvider) addCRL(crl *CRL) { key := crl.CertList.Issuer.ToRDNSequence().String() p.crls[key] = crl } diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index b957a4da84b2..b6fc24ee66ed 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -21,6 +21,7 @@ package advancedtls import ( "crypto/x509" "fmt" + "os" "testing" "time" @@ -34,11 +35,15 @@ const nonCRLFilesUnderCRLDirectory = 5 // unrevoked, revoked leaf, and revoked intermediate chains, as well as a chain // without CRL for issuer, and checks that it’s correctly processed. func TestStaticCRLProvider(t *testing.T) { - p := MakeStaticCRLProvider() + rawCRLs := make([][]byte, 6) for i := 1; i <= 6; i++ { - crl := loadCRL(t, testdata.Path(fmt.Sprintf("crl/%d.crl", i))) - p.AddCRL(crl) + rawCRL, err := os.ReadFile(testdata.Path(fmt.Sprintf("crl/%d.crl", i))) + if err != nil { + t.Fatalf("readFile(%v) failed err = %v", fmt.Sprintf("crl/%d.crl", i), err) + } + rawCRLs = append(rawCRLs, rawCRL) } + p := MakeStaticCRLProvider(rawCRLs) // Each test data entry contains a description of a certificate chain, // certificate chain itself, and if CRL is not expected to be found. tests := []struct { diff --git a/security/advancedtls/crl_test.go b/security/advancedtls/crl_test.go index f76ae5739a63..a0366d928663 100644 --- a/security/advancedtls/crl_test.go +++ b/security/advancedtls/crl_test.go @@ -509,11 +509,15 @@ func TestRevokedCert(t *testing.T) { revokedLeafChain := makeChain(t, testdata.Path("crl/revokedLeaf.pem")) validChain := makeChain(t, testdata.Path("crl/unrevoked.pem")) cache, err := lru.New(5) - cRLProvider := MakeStaticCRLProvider() + rawCRLs := make([][]byte, 6) for i := 1; i <= 6; i++ { - crl := loadCRL(t, testdata.Path(fmt.Sprintf("crl/%d.crl", i))) - cRLProvider.AddCRL(crl) + rawCRL, err := os.ReadFile(testdata.Path(fmt.Sprintf("crl/%d.crl", i))) + if err != nil { + t.Fatalf("readFile(%v) failed err = %v", fmt.Sprintf("crl/%d.crl", i), err) + } + rawCRLs = append(rawCRLs, rawCRL) } + cRLProvider := MakeStaticCRLProvider(rawCRLs) if err != nil { t.Fatalf("lru.New: err = %v", err) } From a4da85eed62ae9c6dda0d3b07b2432b34d73d616 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Wed, 4 Oct 2023 02:41:59 +0000 Subject: [PATCH 23/62] Data race fix --- security/advancedtls/crl_provider.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 103ff8b11af9..f128df807ee1 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -23,6 +23,7 @@ import ( "crypto/x509" "fmt" "os" + "sync" "time" ) @@ -78,8 +79,7 @@ func (p *StaticCRLProvider) addCRL(crl *CRL) { // CRL returns CRL struct if it was previously loaded by calling AddCRL. func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { - key := cert.Issuer.ToRDNSequence().String() - return p.crls[key], nil + return p.crls[cert.Issuer.ToRDNSequence().String()], nil } // Options represents a data structure holding a @@ -95,6 +95,7 @@ type Options struct { type FileWatcherCRLProvider struct { crls map[string]*CRL opts Options + mu sync.Mutex cancel context.CancelFunc } @@ -225,6 +226,8 @@ func (p *FileWatcherCRLProvider) addCRL(filePath string) error { } certList.RawIssuer = rawCRLIssuer key := certList.CertList.Issuer.ToRDNSequence().String() + p.mu.Lock() + defer p.mu.Unlock() p.crls[key] = certList grpclogLogger.Infof("In-memory CRL storage of FileWatcherCRLProvider for key %v updated", key) return nil @@ -233,6 +236,7 @@ func (p *FileWatcherCRLProvider) addCRL(filePath string) error { // CRL retrieves the CRL associated with the given certificate's issuer DN from // in-memory if it was previously loaded during CRLDirectory scan. func (p *FileWatcherCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { - key := cert.Issuer.ToRDNSequence().String() - return p.crls[key], nil + p.mu.Lock() + defer p.mu.Unlock() + return p.crls[cert.Issuer.ToRDNSequence().String()], nil } From c3ba07e3bcb88ce72538f8d254b103d8d3ac13d8 Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Wed, 4 Oct 2023 18:53:29 +0000 Subject: [PATCH 24/62] Test helper fn change --- security/advancedtls/advancedtls_test.go | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/security/advancedtls/advancedtls_test.go b/security/advancedtls/advancedtls_test.go index c0b4123d5121..7a48df80ee18 100644 --- a/security/advancedtls/advancedtls_test.go +++ b/security/advancedtls/advancedtls_test.go @@ -381,18 +381,12 @@ func (s) TestClientServerHandshake(t *testing.T) { return &GetRootCAsResults{TrustCerts: cs.ServerTrust3}, nil } - makeStaticCRLProvider := func(containsRevoked bool) *RevocationConfig { + makeStaticCRLProvider := func(crlPath string) *RevocationConfig { - rawCRLs := make([][]byte, 1) - var path string - if containsRevoked { - path = testdata.Path("crl/provider/crl_server_revoked.pem") - } else { - path = testdata.Path("crl/provider/crl_empty.pem") - } - rawCRL, err := os.ReadFile(path) + rawCRLs := make([][]byte, 0) + rawCRL, err := os.ReadFile(crlPath) if err != nil { - t.Fatalf("readFile(%v) failed err = %v", path, err) + t.Fatalf("readFile(%v) failed err = %v", crlPath, err) } rawCRLs = append(rawCRLs, rawCRL) cRLProvider := MakeStaticCRLProvider(rawCRLs) @@ -744,7 +738,7 @@ func (s) TestClientServerHandshake(t *testing.T) { clientGetRoot: getRootCAsForClientCRL, clientVerifyFunc: clientVerifyFuncGood, clientVType: CertVerification, - clientRevocationConfig: makeStaticCRLProvider(false), + clientRevocationConfig: makeStaticCRLProvider(testdata.Path("crl/provider/crl_empty.pem")), serverMutualTLS: true, serverCert: []tls.Certificate{cs.ServerCert3}, serverGetRoot: getRootCAsForServerCRL, @@ -759,7 +753,7 @@ func (s) TestClientServerHandshake(t *testing.T) { clientGetRoot: getRootCAsForClientCRL, clientVerifyFunc: clientVerifyFuncGood, clientVType: CertVerification, - clientRevocationConfig: makeStaticCRLProvider(true), + clientRevocationConfig: makeStaticCRLProvider(testdata.Path("crl/provider/crl_server_revoked.pem")), serverMutualTLS: true, serverCert: []tls.Certificate{cs.ServerCert3}, serverGetRoot: getRootCAsForServerCRL, From ffe5c34a1cc1df25090b34509bc2464c10b3aa24 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Sun, 8 Oct 2023 23:21:25 +0000 Subject: [PATCH 25/62] Address PR comments --- security/advancedtls/advancedtls_test.go | 7 ++----- security/advancedtls/crl.go | 10 +++++----- security/advancedtls/crl_provider.go | 15 +++++++-------- security/advancedtls/crl_provider_test.go | 4 ++-- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/security/advancedtls/advancedtls_test.go b/security/advancedtls/advancedtls_test.go index 7a48df80ee18..0893a8159935 100644 --- a/security/advancedtls/advancedtls_test.go +++ b/security/advancedtls/advancedtls_test.go @@ -382,14 +382,11 @@ func (s) TestClientServerHandshake(t *testing.T) { } makeStaticCRLProvider := func(crlPath string) *RevocationConfig { - - rawCRLs := make([][]byte, 0) rawCRL, err := os.ReadFile(crlPath) if err != nil { t.Fatalf("readFile(%v) failed err = %v", crlPath, err) } - rawCRLs = append(rawCRLs, rawCRL) - cRLProvider := MakeStaticCRLProvider(rawCRLs) + cRLProvider := MakeStaticCRLProvider([][]byte{rawCRL}) return &RevocationConfig{ AllowUndetermined: true, CRLProvider: cRLProvider, @@ -731,7 +728,7 @@ func (s) TestClientServerHandshake(t *testing.T) { }, // Client: set valid credentials with the revocation config // Server: set valid credentials with the revocation config - // Expected Behavior: success, because non of the certificate chains sent in the connection are revoked + // Expected Behavior: success, because none of the certificate chains sent in the connection are revoked { desc: "Client sets peer cert, reload root function with verifyFuncGood; Server sets peer cert, reload root function; Client uses CRL; mutualTLS", clientCert: []tls.Certificate{cs.ClientCert3}, diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index 8153f1f2f340..a2750d996c7c 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -88,10 +88,10 @@ func (s RevocationStatus) String() string { return [...]string{"RevocationUndetermined", "RevocationUnrevoked", "RevocationRevoked"}[s] } -// CRL contains a pkix.CertificateList and parsed -// extensions that aren't provided by the golang CRL parser. -// GRPC requires certain specifics for CRLs - all CRLs should be loaded using -// NewCRL() for bytes directly or ReadCRLFile() to read directly from a filepath +// CRL contains a pkix.CertificateList and parsed extensions that aren't +// provided by the golang CRL parser. +// All CRLs should be loaded using NewCRL() for bytes directly or ReadCRLFile() +// to read directly from a filepath type CRL struct { CertList *x509.RevocationList // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. @@ -99,7 +99,7 @@ type CRL struct { RawIssuer []byte } -// NewCRL constructs new CRL from provided byte array. +// NewCRL constructs new CRL from the provided byte array. func NewCRL(b []byte) (*CRL, error) { crl, err := parseRevocationList(b) if err != nil { diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index f128df807ee1..e845edd6210e 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -36,7 +36,6 @@ const defaultCRLRefreshDuration = 1 * time.Hour // CRL are loaded and stored. Such implementations can be used in // RevocationConfig of advancedtls.ClientOptions and/or // advancedtls.ServerOptions . -// // TODO(erm-g): Add link to related gRFC once it's ready. // Please refer to https://github.com/grpc/proposal/ for more details. type CRLProvider interface { @@ -87,7 +86,7 @@ func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { type Options struct { CRLDirectory string // Path of the directory containing CRL files RefreshDuration time.Duration // Time interval between CRLDirectory scans - cRLReloadingFailedCallback func(err error) // Custom callback executed when a CRL file can’t be processed + crlReloadingFailedCallback func(err error) // Custom callback executed when a CRL file can’t be processed } // FileWatcherCRLProvider implements the CRLProvider interface by periodically scanning @@ -175,8 +174,8 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { dir, err := os.Open(p.opts.CRLDirectory) if err != nil { grpclogLogger.Errorf("Can't open CRLDirectory %v", p.opts.CRLDirectory, err) - if p.opts.cRLReloadingFailedCallback != nil { - p.opts.cRLReloadingFailedCallback(err) + if p.opts.crlReloadingFailedCallback != nil { + p.opts.crlReloadingFailedCallback(err) } } defer dir.Close() @@ -184,8 +183,8 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { files, err := dir.ReadDir(0) if err != nil { grpclogLogger.Errorf("Can't access files under CRLDirectory %v", p.opts.CRLDirectory, err) - if p.opts.cRLReloadingFailedCallback != nil { - p.opts.cRLReloadingFailedCallback(err) + if p.opts.crlReloadingFailedCallback != nil { + p.opts.crlReloadingFailedCallback(err) } } @@ -197,8 +196,8 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { if err != nil { failCounter++ grpclogLogger.Warningf("Can't add CRL from file %v under CRLDirectory %v", filePath, p.opts.CRLDirectory, err) - if p.opts.cRLReloadingFailedCallback != nil { - p.opts.cRLReloadingFailedCallback(err) + if p.opts.crlReloadingFailedCallback != nil { + p.opts.crlReloadingFailedCallback(err) } continue } diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index b6fc24ee66ed..225fbd902cbe 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -110,7 +110,7 @@ func TestFileWatcherCRLProviderConfig(t *testing.T) { regularProvider, err := MakeFileWatcherCRLProvider(Options{ CRLDirectory: testdata.Path("crl"), RefreshDuration: 5 * time.Second, - cRLReloadingFailedCallback: customCallback, + crlReloadingFailedCallback: customCallback, }) if err != nil { t.Fatal("Unexpected error while creating regular FileWatcherCRLProvider:", err) @@ -133,7 +133,7 @@ func TestFileWatcherCRLProvider(t *testing.T) { p, err := MakeFileWatcherCRLProvider(Options{ CRLDirectory: testdata.Path("crl"), RefreshDuration: 5 * time.Second, - cRLReloadingFailedCallback: customCallback, + crlReloadingFailedCallback: customCallback, }) if err != nil { t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) From 6d2818171f9eabfae86d335085c2d104625d0f5f Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 9 Oct 2023 15:52:25 +0000 Subject: [PATCH 26/62] Address PR comments (part 2) --- security/advancedtls/crl.go | 33 ++++++++++----------- security/advancedtls/crl_deprecated_test.go | 16 +++++----- security/advancedtls/crl_provider.go | 6 ++-- security/advancedtls/crl_test.go | 14 ++++----- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index a2750d996c7c..89f403d90b51 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -93,10 +93,10 @@ func (s RevocationStatus) String() string { // All CRLs should be loaded using NewCRL() for bytes directly or ReadCRLFile() // to read directly from a filepath type CRL struct { - CertList *x509.RevocationList + certList *x509.RevocationList // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. - AuthorityKeyID []byte - RawIssuer []byte + authorityKeyID []byte + rawIssuer []byte } // NewCRL constructs new CRL from the provided byte array. @@ -109,7 +109,7 @@ func NewCRL(b []byte) (*CRL, error) { if err != nil { return nil, fmt.Errorf("parseCRLExtensions() failed err = %v", err) } - crlExt.RawIssuer, err = extractCRLIssuer(b) + crlExt.rawIssuer, err = extractCRLIssuer(b) if err != nil { return nil, fmt.Errorf("extractCRLIssuer() failed err= %v", err) } @@ -263,7 +263,7 @@ func cachedCrl(rawIssuer []byte, cache Cache) (*CRL, bool) { return nil, false } // If the CRL is expired, force a reload. - if hasExpired(crl.CertList, time.Now()) { + if hasExpired(crl.certList, time.Now()) { return nil, false } return crl, true @@ -298,7 +298,6 @@ func fetchCRL(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg Revocat return nil, fmt.Errorf("CrlProvider failed err = %v", err) } if crl == nil { - // TODO print out cert info here? What cert contents are okay to have in a log? return nil, fmt.Errorf("no CRL found for certificate's issuer") } return crl, nil @@ -319,12 +318,12 @@ func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg Revoca // problem - it's not invalid to have no CRLs if you don't have any // revocations for an issuer. We just return RevocationUndetermined and // there is a setting for the user to control the handling of that. - grpclogLogger.Warningf("fetchCRL(%v) err = %v", c.Issuer, err) + grpclogLogger.Warningf("fetchCRL() err = %v", err) return RevocationUndetermined } revocation, err := checkCertRevocation(c, crl) if err != nil { - grpclogLogger.Warningf("checkCertRevocation(CRL %v) failed: %v", crl.CertList.Issuer, err) + grpclogLogger.Warningf("checkCertRevocation(CRL %v) failed: %v", crl.certList.Issuer, err) // We couldn't check the CRL file for some reason, so we don't know if it's RevocationUnrevoked or not. return RevocationUndetermined } @@ -337,10 +336,10 @@ func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg Revoca func checkCertRevocation(c *x509.Certificate, crl *CRL) (RevocationStatus, error) { // Per section 5.3.3 we prime the certificate issuer with the CRL issuer. // Subsequent entries use the previous entry's issuer. - rawEntryIssuer := crl.RawIssuer + rawEntryIssuer := crl.rawIssuer // Loop through all the revoked certificates. - for _, revCert := range crl.CertList.RevokedCertificates { + for _, revCert := range crl.certList.RevokedCertificates { // 5.3 Loop through CRL entry extensions for needed information. for _, ext := range revCert.Extensions { if oidCertificateIssuer.Equal(ext.Id) { @@ -436,7 +435,7 @@ func parseCRLExtensions(c *x509.RevocationList) (*CRL, error) { if c == nil { return nil, errors.New("c is nil, expected any value") } - certList := &CRL{CertList: c} + certList := &CRL{certList: c} for _, ext := range c.Extensions { switch { @@ -450,7 +449,7 @@ func parseCRLExtensions(c *x509.RevocationList) (*CRL, error) { } else if len(rest) != 0 { return nil, errors.New("trailing data after AKID extension") } - certList.AuthorityKeyID = a.ID + certList.authorityKeyID = a.ID case oidIssuingDistributionPoint.Equal(ext.Id): var dp issuingDistributionPoint @@ -475,7 +474,7 @@ func parseCRLExtensions(c *x509.RevocationList) (*CRL, error) { } } - if len(certList.AuthorityKeyID) == 0 { + if len(certList.authorityKeyID) == 0 { return nil, errors.New("authority key identifier extension missing") } return certList, nil @@ -517,7 +516,7 @@ func fetchCRLOpenSSLHashDir(rawIssuer []byte, cfg RevocationConfig) (*CRL, error if err != nil { return nil, err } - certList.RawIssuer = rawCRLIssuer + certList.rawIssuer = rawCRLIssuer // RFC5280, 6.3.3 (b) Verify the issuer and scope of the complete CRL. if bytes.Equal(rawIssuer, rawCRLIssuer) { parsedCRL = certList @@ -544,12 +543,12 @@ func verifyCRL(crl *CRL, rawIssuer []byte, chain []*x509.Certificate) error { // "Conforming CRL issuers MUST use the key identifier method, and MUST // include this extension in all CRLs issued." // So, this is much simpler than RFC4158 and should be compatible. - if bytes.Equal(c.SubjectKeyId, crl.AuthorityKeyID) && bytes.Equal(c.RawSubject, crl.RawIssuer) { + if bytes.Equal(c.SubjectKeyId, crl.authorityKeyID) && bytes.Equal(c.RawSubject, crl.rawIssuer) { // RFC5280, 6.3.3 (g) Validate signature. - return crl.CertList.CheckSignatureFrom(c) + return crl.certList.CheckSignatureFrom(c) } } - return fmt.Errorf("verifyCRL: No certificates mached CRL issuer (%v)", crl.CertList.Issuer) + return fmt.Errorf("verifyCRL: No certificates mached CRL issuer (%v)", crl.certList.Issuer) } // pemType is the type of a PEM encoded CRL. diff --git a/security/advancedtls/crl_deprecated_test.go b/security/advancedtls/crl_deprecated_test.go index e5ee05c62cc7..e16ec7ef2599 100644 --- a/security/advancedtls/crl_deprecated_test.go +++ b/security/advancedtls/crl_deprecated_test.go @@ -223,7 +223,7 @@ qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 if err != nil { t.Fatalf("x509.ParseCRL(dummyCrlFile) failed: %v", err) } - crlExt := &CRL{CertList: crl} + crlExt := &CRL{certList: crl} var crlIssuer pkix.Name crlIssuer.FillFromRDNSequence(&crl.TBSCertList.Issuer) @@ -352,7 +352,7 @@ func loadCRL(t *testing.T, path string) *CRL { if err != nil { t.Fatalf("parseCRLExtensions(%v) failed err = %v", path, err) } - crlExt.RawIssuer, err = extractCRLIssuer(b) + crlExt.rawIssuer, err = extractCRLIssuer(b) if err != nil { t.Fatalf("extractCRLIssuer(%v) failed err= %v", path, err) } @@ -373,7 +373,7 @@ func TestCachedCRL(t *testing.T) { { desc: "Valid", val: &CRL{ - CertList: &pkix.CertificateList{ + certList: &pkix.CertificateList{ TBSCertList: pkix.TBSCertificateList{ NextUpdate: time.Now().Add(time.Hour), }, @@ -383,7 +383,7 @@ func TestCachedCRL(t *testing.T) { { desc: "Expired", val: &CRL{ - CertList: &pkix.CertificateList{ + certList: &pkix.CertificateList{ TBSCertList: pkix.TBSCertificateList{ NextUpdate: time.Now().Add(-time.Hour), }, @@ -460,7 +460,7 @@ func TestGetIssuerCRLCache(t *testing.T) { func TestVerifyCrl(t *testing.T) { tampered := loadCRL(t, testdata.Path("crl/1.crl")) // Change the signature so it won't verify - tampered.CertList.SignatureValue.Bytes[0]++ + tampered.certList.SignatureValue.Bytes[0]++ verifyTests := []struct { desc string @@ -755,8 +755,8 @@ func TestCRLCacheExpirationReloading(t *testing.T) { // `3.crl`` revokes `revokedInt.pem` crl := loadCRL(t, testdata.Path("crl/3.crl")) // Modify the crl so that the cert is NOT revoked and add it to the cache - crl.CertList.TBSCertList.RevokedCertificates = nil - crl.CertList.TBSCertList.NextUpdate = time.Now().Add(time.Hour) + crl.certList.TBSCertList.RevokedCertificates = nil + crl.certList.TBSCertList.NextUpdate = time.Now().Add(time.Hour) cache.Add(hex.EncodeToString(rawIssuer), crl) var cfg = RevocationConfig{RootDir: testdata.Path("crl"), Cache: cache} revocationStatus := checkChain(certs, cfg) @@ -765,7 +765,7 @@ func TestCRLCacheExpirationReloading(t *testing.T) { } // Modify the entry in the cache so that the cache will be refreshed - crl.CertList.TBSCertList.NextUpdate = time.Now() + crl.certList.TBSCertList.NextUpdate = time.Now() cache.Add(hex.EncodeToString(rawIssuer), crl) revocationStatus = checkChain(certs, cfg) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index e845edd6210e..79b02fdff5c5 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -72,7 +72,7 @@ func MakeStaticCRLProvider(rawCRLs [][]byte) *StaticCRLProvider { // AddCRL adds/updates provided CRL to in-memory storage. func (p *StaticCRLProvider) addCRL(crl *CRL) { - key := crl.CertList.Issuer.ToRDNSequence().String() + key := crl.certList.Issuer.ToRDNSequence().String() p.crls[key] = crl } @@ -223,8 +223,8 @@ func (p *FileWatcherCRLProvider) addCRL(filePath string) error { if err != nil { return fmt.Errorf("addCRL: can't extract Issuer from CRL from file %v: %v", filePath, err) } - certList.RawIssuer = rawCRLIssuer - key := certList.CertList.Issuer.ToRDNSequence().String() + certList.rawIssuer = rawCRLIssuer + key := certList.certList.Issuer.ToRDNSequence().String() p.mu.Lock() defer p.mu.Unlock() p.crls[key] = certList diff --git a/security/advancedtls/crl_test.go b/security/advancedtls/crl_test.go index a0366d928663..4f31f4bad0d2 100644 --- a/security/advancedtls/crl_test.go +++ b/security/advancedtls/crl_test.go @@ -223,7 +223,7 @@ qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 if err != nil { t.Fatalf("parseRevocationList(dummyCrlFile) failed: %v", err) } - crlExt := &CRL{CertList: crl} + crlExt := &CRL{certList: crl} var crlIssuer pkix.Name = crl.Issuer var revocationTests = []struct { @@ -360,7 +360,7 @@ func TestCachedCRL(t *testing.T) { { desc: "Valid", val: &CRL{ - CertList: &x509.RevocationList{ + certList: &x509.RevocationList{ NextUpdate: time.Now().Add(time.Hour), }}, ok: true, @@ -368,7 +368,7 @@ func TestCachedCRL(t *testing.T) { { desc: "Expired", val: &CRL{ - CertList: &x509.RevocationList{ + certList: &x509.RevocationList{ NextUpdate: time.Now().Add(-time.Hour), }}, ok: false, @@ -443,7 +443,7 @@ func TestGetIssuerCRLCache(t *testing.T) { func TestVerifyCrl(t *testing.T) { tampered := loadCRL(t, testdata.Path("crl/1.crl")) // Change the signature so it won't verify - tampered.CertList.Signature[0]++ + tampered.certList.Signature[0]++ verifyTests := []struct { desc string @@ -766,8 +766,8 @@ func TestCRLCacheExpirationReloading(t *testing.T) { // `3.crl`` revokes `revokedInt.pem` crl := loadCRL(t, testdata.Path("crl/3.crl")) // Modify the crl so that the cert is NOT revoked and add it to the cache - crl.CertList.RevokedCertificates = nil - crl.CertList.NextUpdate = time.Now().Add(time.Hour) + crl.certList.RevokedCertificates = nil + crl.certList.NextUpdate = time.Now().Add(time.Hour) cache.Add(hex.EncodeToString(rawIssuer), crl) var cfg = RevocationConfig{RootDir: testdata.Path("crl"), Cache: cache} revocationStatus := checkChain(certs, cfg) @@ -776,7 +776,7 @@ func TestCRLCacheExpirationReloading(t *testing.T) { } // Modify the entry in the cache so that the cache will be refreshed - crl.CertList.NextUpdate = time.Now() + crl.certList.NextUpdate = time.Now() cache.Add(hex.EncodeToString(rawIssuer), crl) revocationStatus = checkChain(certs, cfg) From 7814373f761925ce4c7024399e921c2f3cbf4f44 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 9 Oct 2023 21:17:19 +0000 Subject: [PATCH 27/62] Migration from context to channel for controlling crl reloading goroutine --- security/advancedtls/crl_provider.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 79b02fdff5c5..0ec229ab02e0 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -19,7 +19,6 @@ package advancedtls import ( - "context" "crypto/x509" "fmt" "os" @@ -92,10 +91,10 @@ type Options struct { // FileWatcherCRLProvider implements the CRLProvider interface by periodically scanning // CRLDirectory (see Options) and storing CRL structs in-memory type FileWatcherCRLProvider struct { - crls map[string]*CRL - opts Options - mu sync.Mutex - cancel context.CancelFunc + crls map[string]*CRL + opts Options + mu sync.Mutex + done chan struct{} } // MakeFileWatcherCRLProvider returns a new instance of the @@ -105,13 +104,13 @@ func MakeFileWatcherCRLProvider(o Options) (*FileWatcherCRLProvider, error) { if err := o.validate(); err != nil { return nil, err } - ctx, cancel := context.WithCancel(context.Background()) + done := make(chan struct{}) provider := &FileWatcherCRLProvider{ crls: make(map[string]*CRL), opts: o, + done: done, } - provider.cancel = cancel - go provider.run(ctx) + go provider.run() return provider, nil } @@ -146,14 +145,14 @@ func (o *Options) validate() error { } // Start starts watching the directory for CRL files and updates the provider accordingly. -func (p *FileWatcherCRLProvider) run(ctx context.Context) { +func (p *FileWatcherCRLProvider) run() { ticker := time.NewTicker(p.opts.RefreshDuration) defer ticker.Stop() p.ScanCRLDirectory() for { select { - case <-ctx.Done(): + case <-p.done: ticker.Stop() return case <-ticker.C: @@ -164,7 +163,7 @@ func (p *FileWatcherCRLProvider) run(ctx context.Context) { // Close stops the background refresh of CRLDirectory of FileWatcherCRLProvider func (p *FileWatcherCRLProvider) Close() { - p.cancel() + close(p.done) } // ScanCRLDirectory starts the process of scanning Options.CRLDirectory and From 1a46b65a1c9f586e2a36e84336f78a0ea43e5e21 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 10 Oct 2023 16:32:43 +0000 Subject: [PATCH 28/62] Align in-memory CRL updates during directory scan to C++ behavior --- security/advancedtls/crl_provider.go | 44 +++++++++++----------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 0ec229ab02e0..a516255d4627 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -187,11 +187,12 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { } } + tempCRLs := make(map[string]*CRL) successCounter := 0 failCounter := 0 for _, file := range files { filePath := fmt.Sprintf("%s/%s", p.opts.CRLDirectory, file.Name()) - err := p.addCRL(filePath) + crl, err := ReadCRLFile(filePath) if err != nil { failCounter++ grpclogLogger.Warningf("Can't add CRL from file %v under CRLDirectory %v", filePath, p.opts.CRLDirectory, err) @@ -200,35 +201,24 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { } continue } + tempCRLs[crl.certList.Issuer.ToRDNSequence().String()] = crl successCounter++ } - grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files tried, %v CRLs added, %v files failed", len(files), successCounter, failCounter) -} - -func (p *FileWatcherCRLProvider) addCRL(filePath string) error { - crlBytes, err := os.ReadFile(filePath) - if err != nil { - return err - } - crl, err := parseRevocationList(crlBytes) - if err != nil { - return fmt.Errorf("addCRL: can't parse CRL from file %v: %v", filePath, err) - } - var certList *CRL - if certList, err = parseCRLExtensions(crl); err != nil { - return fmt.Errorf("addCRL: unsupported CRL %v: %v", filePath, err) - } - rawCRLIssuer, err := extractCRLIssuer(crlBytes) - if err != nil { - return fmt.Errorf("addCRL: can't extract Issuer from CRL from file %v: %v", filePath, err) + if len(files) == successCounter { + var updatedCRLs = &tempCRLs + var _ = &p.crls + p.mu.Lock() + defer p.mu.Unlock() + _ = updatedCRLs + grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files found and processed successfully, in-memory CRL storage flushed and repopulated", p.opts.CRLDirectory, len(files)) + } else { + p.mu.Lock() + defer p.mu.Unlock() + for key, value := range tempCRLs { + p.crls[key] = value + } + grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files found, %v files processing failed, %v entries of in-memory CRL storage added/updated", p.opts.CRLDirectory, len(files), failCounter, successCounter) } - certList.rawIssuer = rawCRLIssuer - key := certList.certList.Issuer.ToRDNSequence().String() - p.mu.Lock() - defer p.mu.Unlock() - p.crls[key] = certList - grpclogLogger.Infof("In-memory CRL storage of FileWatcherCRLProvider for key %v updated", key) - return nil } // CRL retrieves the CRL associated with the given certificate's issuer DN from From d7f1555d048630f656d81568e0be9eb8820a0446 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 10 Oct 2023 17:05:08 +0000 Subject: [PATCH 29/62] Improve comments for ScanCRLDirectory --- security/advancedtls/crl_provider.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index a516255d4627..4cf002233771 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -167,8 +167,10 @@ func (p *FileWatcherCRLProvider) Close() { } // ScanCRLDirectory starts the process of scanning Options.CRLDirectory and -// updating in-memory storage of CRL structs.Please note that the same method is +// updating in-memory storage of CRL structs. Please note that the same method is // called periodically by run goroutine. +// TODO(erm-g): Add link to related gRFC once it's ready. +// Please refer to https://github.com/grpc/proposal/ for edge case details. func (p *FileWatcherCRLProvider) ScanCRLDirectory() { dir, err := os.Open(p.opts.CRLDirectory) if err != nil { @@ -204,6 +206,8 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { tempCRLs[crl.certList.Issuer.ToRDNSequence().String()] = crl successCounter++ } + // Only if all the files are processed successful we can swap maps (there + // might be deletions of entries in this case if len(files) == successCounter { var updatedCRLs = &tempCRLs var _ = &p.crls @@ -212,6 +216,7 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { _ = updatedCRLs grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files found and processed successfully, in-memory CRL storage flushed and repopulated", p.opts.CRLDirectory, len(files)) } else { + // Since some of the files failed we can only add/update entries in the map p.mu.Lock() defer p.mu.Unlock() for key, value := range tempCRLs { From 2f1935d04b9b88a56ef35457d4e92bb8207dd6cc Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 10 Oct 2023 18:54:58 +0000 Subject: [PATCH 30/62] Base test case for Scan CRL Directory file manipulations --- security/advancedtls/crl_provider.go | 7 +- security/advancedtls/crl_provider_test.go | 86 +++++++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 4cf002233771..df595b83f770 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -209,11 +209,12 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { // Only if all the files are processed successful we can swap maps (there // might be deletions of entries in this case if len(files) == successCounter { - var updatedCRLs = &tempCRLs - var _ = &p.crls p.mu.Lock() defer p.mu.Unlock() - _ = updatedCRLs + p.crls = make(map[string]*CRL) + for key, value := range tempCRLs { + p.crls[key] = value + } grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files found and processed successfully, in-memory CRL storage flushed and repopulated", p.opts.CRLDirectory, len(files)) } else { // Since some of the files failed we can only add/update entries in the map diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 225fbd902cbe..9c78ba1bba16 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -21,10 +21,13 @@ package advancedtls import ( "crypto/x509" "fmt" + "io" "os" + "path/filepath" "testing" "time" + "github.com/google/go-cmp/cmp" "google.golang.org/grpc/security/advancedtls/testdata" ) @@ -184,3 +187,86 @@ func TestFileWatcherCRLProvider(t *testing.T) { } p.Close() } + +// TestFileWatcherCRLProviderDirectoryScan tests how FileWatcherCRLProvider +// handles different contents of Options.CRLDirectory +// We update the content with new (correct and incorrect) CRL files and check if +// in-memory storage was properly updated +func TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { + sourcePath := testdata.Path("crl") + targetPath := testdata.Path("crl/provider/filewatcher") + p, err := MakeFileWatcherCRLProvider(Options{ + CRLDirectory: targetPath, + RefreshDuration: 1 * time.Hour, + }) + if err != nil { + t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) + } + + tests := []struct { + desc string + fileNames []string + expectedEntries int + }{ + { + desc: "Empty dir", + fileNames: []string{}, + expectedEntries: 0, + }, + { + desc: "Simple addition", + fileNames: []string{"1.crl"}, + expectedEntries: 1, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + copyFiles(sourcePath, targetPath, tt.fileNames, t) + p.ScanCRLDirectory() + if diff := cmp.Diff(len(p.crls), tt.expectedEntries); diff != "" { + t.Errorf("Expected number of entries in the map do not match\ndiff (-got +want):\n%s", diff) + } + }) + } + + p.Close() +} + +func copyFiles(sourcePath string, targetPath string, fileNames []string, t *testing.T) { + targetDir, err := os.Open(targetPath) + if err != nil { + t.Fatalf("Can't open dir %v: %v", targetPath, err) + } + defer targetDir.Close() + names, err := targetDir.Readdirnames(-1) + if err != nil { + t.Fatalf("Can't read dir %v: %v", targetPath, err) + } + for _, name := range names { + err = os.RemoveAll(filepath.Join(testdata.Path(targetPath), name)) + if err != nil { + t.Fatalf("Can't remove file %v: %v", name, err) + } + } + for _, fileName := range fileNames { + destinationPath := filepath.Join(targetPath, fileName) + + sourceFile, err := os.Open(filepath.Join(sourcePath, fileName)) + if err != nil { + t.Fatalf("Can't open file %v: %v", fileName, err) + } + defer sourceFile.Close() + + destinationFile, err := os.Create(destinationPath) + if err != nil { + t.Fatalf("Can't create file %v: %v", destinationFile, err) + } + defer destinationFile.Close() + + _, err = io.Copy(destinationFile, sourceFile) + if err != nil { + t.Fatalf("Can't copy file %v to %v: %v", sourceFile, destinationFile, err) + } + } +} From 0a7b0865ed65aeb7ddebba40ff563f358e5ee9ac Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 10 Oct 2023 20:30:57 +0000 Subject: [PATCH 31/62] full set of cases for CRL directory content manipulation --- security/advancedtls/crl_provider_test.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 9c78ba1bba16..cc43956dfcae 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -218,6 +218,26 @@ func TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { fileNames: []string{"1.crl"}, expectedEntries: 1, }, + { + desc: "Addition and deletion", + fileNames: []string{"2.crl", "3.crl"}, + expectedEntries: 2, + }, + { + desc: "Addition and updating", + fileNames: []string{"2.crl", "3.crl", "4.crl"}, + expectedEntries: 3, + }, + { + desc: "Addition and a corrupt file", + fileNames: []string{"5.crl", "README.md"}, + expectedEntries: 4, + }, + { + desc: "Full deletion", + fileNames: []string{}, + expectedEntries: 0, + }, } for _, tt := range tests { From 8d05f289c0c29da244c1f6f8cfda11f3dcba9864 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 10 Oct 2023 21:01:16 +0000 Subject: [PATCH 32/62] Add comment for table test structure --- security/advancedtls/crl_provider_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index cc43956dfcae..388226cca30a 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -191,7 +191,9 @@ func TestFileWatcherCRLProvider(t *testing.T) { // TestFileWatcherCRLProviderDirectoryScan tests how FileWatcherCRLProvider // handles different contents of Options.CRLDirectory // We update the content with new (correct and incorrect) CRL files and check if -// in-memory storage was properly updated +// in-memory storage was properly updated. Please note that the same instance of +// FileWatcherCRLProvider is used for the whole test so test cases are not +// independent from each other func TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { sourcePath := testdata.Path("crl") targetPath := testdata.Path("crl/provider/filewatcher") @@ -203,6 +205,9 @@ func TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) } + // Each test data entry contains a description of CRL directory content, name + // of the files to be copied there before the test case, and expected number + // of entries in the FileWatcherCRLProvider map. tests := []struct { desc string fileNames []string From ccbf7f60756c6e96a42b1eb646bdf87b1355d5d6 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Wed, 11 Oct 2023 01:20:49 +0000 Subject: [PATCH 33/62] Fix for go.mod and go.sum --- security/advancedtls/crl_provider_test.go | 12 ++++++------ security/advancedtls/go.mod | 1 + security/advancedtls/go.sum | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 388226cca30a..366fe3af6d3d 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -190,10 +190,10 @@ func TestFileWatcherCRLProvider(t *testing.T) { // TestFileWatcherCRLProviderDirectoryScan tests how FileWatcherCRLProvider // handles different contents of Options.CRLDirectory -// We update the content with new (correct and incorrect) CRL files and check if -// in-memory storage was properly updated. Please note that the same instance of -// FileWatcherCRLProvider is used for the whole test so test cases are not -// independent from each other +// We update the content with various (correct and incorrect) CRL files and +// check if in-memory storage was properly updated. Please note that the same +// instance of FileWatcherCRLProvider is used for the whole test so test cases +// cases are not independent from each other. func TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { sourcePath := testdata.Path("crl") targetPath := testdata.Path("crl/provider/filewatcher") @@ -206,8 +206,8 @@ func TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { } // Each test data entry contains a description of CRL directory content, name - // of the files to be copied there before the test case, and expected number - // of entries in the FileWatcherCRLProvider map. + // of the files to be copied there before the test case execution, and + // expected number of entries in the FileWatcherCRLProvider map. tests := []struct { desc string fileNames []string diff --git a/security/advancedtls/go.mod b/security/advancedtls/go.mod index 928f86fff832..2464b1d75828 100644 --- a/security/advancedtls/go.mod +++ b/security/advancedtls/go.mod @@ -3,6 +3,7 @@ module google.golang.org/grpc/security/advancedtls go 1.17 require ( + github.com/google/go-cmp v0.5.9 github.com/hashicorp/golang-lru v0.5.4 golang.org/x/crypto v0.8.0 google.golang.org/grpc v1.54.0 diff --git a/security/advancedtls/go.sum b/security/advancedtls/go.sum index 2766a3d8223a..58858a22f707 100644 --- a/security/advancedtls/go.sum +++ b/security/advancedtls/go.sum @@ -3,6 +3,7 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= From 99ecab06671e58a231220fe78d72f2a2466659f1 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Wed, 11 Oct 2023 01:36:13 +0000 Subject: [PATCH 34/62] Empty directoru workaround --- .../advancedtls/testdata/crl/provider/filewatcher/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 security/advancedtls/testdata/crl/provider/filewatcher/.gitignore diff --git a/security/advancedtls/testdata/crl/provider/filewatcher/.gitignore b/security/advancedtls/testdata/crl/provider/filewatcher/.gitignore new file mode 100644 index 000000000000..86d0cb2726c6 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/filewatcher/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file From 9e5a70d331f38b5e7013ab388b619c9b3ff1df22 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Wed, 11 Oct 2023 15:09:27 +0000 Subject: [PATCH 35/62] Delete deprecated crl functionality --- security/advancedtls/crl_deprecated.go | 521 ------------- security/advancedtls/crl_deprecated_test.go | 775 -------------------- security/advancedtls/crl_provider_test.go | 2 +- 3 files changed, 1 insertion(+), 1297 deletions(-) delete mode 100644 security/advancedtls/crl_deprecated.go delete mode 100644 security/advancedtls/crl_deprecated_test.go diff --git a/security/advancedtls/crl_deprecated.go b/security/advancedtls/crl_deprecated.go deleted file mode 100644 index cd7b3d1228ad..000000000000 --- a/security/advancedtls/crl_deprecated.go +++ /dev/null @@ -1,521 +0,0 @@ -// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported -//go:build !go1.19 - -/* - * - * Copyright 2021 gRPC authors. - * - * 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 advancedtls - -import ( - "bytes" - "crypto/sha1" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/binary" - "encoding/hex" - "encoding/pem" - "errors" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - "golang.org/x/crypto/cryptobyte" - cbasn1 "golang.org/x/crypto/cryptobyte/asn1" - "google.golang.org/grpc/grpclog" -) - -var grpclogLogger = grpclog.Component("advancedtls") - -// Cache is an interface to cache CRL files. -// The cache implementation must be concurrency safe. -// A fixed size lru cache from golang-lru is recommended. -type Cache interface { - // Add adds a value to the cache. - Add(key, value interface{}) bool - // Get looks up a key's value from the cache. - Get(key interface{}) (value interface{}, ok bool) -} - -// RevocationConfig contains options for CRL lookup. -type RevocationConfig struct { - // RootDir is the directory to search for CRL files. - // Directory format must match OpenSSL X509_LOOKUP_hash_dir(3). - RootDir string - // AllowUndetermined controls if certificate chains with RevocationUndetermined - // revocation status are allowed to complete. - AllowUndetermined bool - // Cache will store CRL files if not nil, otherwise files are reloaded for every lookup. - Cache Cache -} - -// RevocationStatus is the revocation status for a certificate or chain. -type RevocationStatus int - -const ( - // RevocationUndetermined means we couldn't find or verify a CRL for the cert. - RevocationUndetermined RevocationStatus = iota - // RevocationUnrevoked means we found the CRL for the cert and the cert is not revoked. - RevocationUnrevoked - // RevocationRevoked means we found the CRL and the cert is revoked. - RevocationRevoked -) - -func (s RevocationStatus) String() string { - return [...]string{"RevocationUndetermined", "RevocationUnrevoked", "RevocationRevoked"}[s] -} - -// CRL contains a pkix.CertificateList and parsed -// extensions that aren't provided by the golang CRL parser. -type CRL struct { - CertList *pkix.CertificateList - // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. - AuthorityKeyID []byte - RawIssuer []byte -} - -const tagDirectoryName = 4 - -var ( - // RFC5280, 5.2.4 id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } - oidDeltaCRLIndicator = asn1.ObjectIdentifier{2, 5, 29, 27} - // RFC5280, 5.2.5 id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } - oidIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28} - // RFC5280, 5.3.3 id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } - oidCertificateIssuer = asn1.ObjectIdentifier{2, 5, 29, 29} - // RFC5290, 4.2.1.1 id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } - oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35} -) - -// x509NameHash implements the OpenSSL X509_NAME_hash function for hashed directory lookups. -// -// NOTE: due to the behavior of asn1.Marshal, if the original encoding of the RDN sequence -// contains strings which do not use the ASN.1 PrintableString type, the name will not be -// re-encoded using those types, resulting in a hash which does not match that produced -// by OpenSSL. -func x509NameHash(r pkix.RDNSequence) string { - var canonBytes []byte - // First, canonicalize all the strings. - for _, rdnSet := range r { - for i, rdn := range rdnSet { - value, ok := rdn.Value.(string) - if !ok { - continue - } - // OpenSSL trims all whitespace, does a tolower, and removes extra spaces between words. - // Implemented in x509_name_canon in OpenSSL - canonStr := strings.Join(strings.Fields( - strings.TrimSpace(strings.ToLower(value))), " ") - // Then it changes everything to UTF8 strings - rdnSet[i].Value = asn1.RawValue{Tag: asn1.TagUTF8String, Bytes: []byte(canonStr)} - - } - } - - // Finally, OpenSSL drops the initial sequence tag - // so we marshal all the RDNs separately instead of as a group. - for _, canonRdn := range r { - b, err := asn1.Marshal(canonRdn) - if err != nil { - continue - } - canonBytes = append(canonBytes, b...) - } - - issuerHash := sha1.Sum(canonBytes) - // Openssl takes the first 4 bytes and encodes them as a little endian - // uint32 and then uses the hex to make the file name. - // In C++, this would be: - // (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | - // ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) - // ) & 0xffffffffL; - fileHash := binary.LittleEndian.Uint32(issuerHash[0:4]) - return fmt.Sprintf("%08x", fileHash) -} - -// CheckRevocation checks the connection for revoked certificates based on RFC5280. -// This implementation has the following major limitations: -// - Indirect CRL files are not supported. -// - CRL loading is only supported from directories in the X509_LOOKUP_hash_dir format. -// - OnlySomeReasons is not supported. -// - Delta CRL files are not supported. -// - Certificate CRLDistributionPoint must be URLs, but are then ignored and converted into a file path. -// - CRL checks are done after path building, which goes against RFC4158. -func CheckRevocation(conn tls.ConnectionState, cfg RevocationConfig) error { - return CheckChainRevocation(conn.VerifiedChains, cfg) -} - -// CheckChainRevocation checks the verified certificate chain -// for revoked certificates based on RFC5280. -func CheckChainRevocation(verifiedChains [][]*x509.Certificate, cfg RevocationConfig) error { - // Iterate the verified chains looking for one that is RevocationUnrevoked. - // A single RevocationUnrevoked chain is enough to allow the connection, and a single RevocationRevoked - // chain does not mean the connection should fail. - count := make(map[RevocationStatus]int) - for _, chain := range verifiedChains { - switch checkChain(chain, cfg) { - case RevocationUnrevoked: - // If any chain is RevocationUnrevoked then return no error. - return nil - case RevocationRevoked: - // If this chain is revoked, keep looking for another chain. - count[RevocationRevoked]++ - continue - case RevocationUndetermined: - if cfg.AllowUndetermined { - return nil - } - count[RevocationUndetermined]++ - continue - } - } - return fmt.Errorf("no unrevoked chains found: %v", count) -} - -// checkChain will determine and check all certificates in chain against the CRL -// defined in the certificate with the following rules: -// 1. If any certificate is RevocationRevoked, return RevocationRevoked. -// 2. If any certificate is RevocationUndetermined, return RevocationUndetermined. -// 3. If all certificates are RevocationUnrevoked, return RevocationUnrevoked. -func checkChain(chain []*x509.Certificate, cfg RevocationConfig) RevocationStatus { - chainStatus := RevocationUnrevoked - for _, c := range chain { - switch checkCert(c, chain, cfg) { - case RevocationRevoked: - // Easy case, if a cert in the chain is revoked, the chain is revoked. - return RevocationRevoked - case RevocationUndetermined: - // If we couldn't find the revocation status for a cert, the chain is at best RevocationUndetermined - // keep looking to see if we find a cert in the chain that's RevocationRevoked, - // but return RevocationUndetermined at a minimum. - chainStatus = RevocationUndetermined - case RevocationUnrevoked: - // Continue iterating up the cert chain. - continue - } - } - return chainStatus -} - -func cachedCrl(rawIssuer []byte, cache Cache) (*CRL, bool) { - val, ok := cache.Get(hex.EncodeToString(rawIssuer)) - if !ok { - return nil, false - } - crl, ok := val.(*CRL) - if !ok { - return nil, false - } - // If the CRL is expired, force a reload. - if crl.CertList.HasExpired(time.Now()) { - return nil, false - } - return crl, true -} - -// fetchIssuerCRL fetches and verifies the CRL for rawIssuer from disk or cache if configured in cfg. -func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*CRL, error) { - if cfg.Cache != nil { - if crl, ok := cachedCrl(rawIssuer, cfg.Cache); ok { - return crl, nil - } - } - - crl, err := fetchCRL(rawIssuer, cfg) - if err != nil { - return nil, fmt.Errorf("fetchCRL() failed: %v", err) - } - - if err := verifyCRL(crl, rawIssuer, crlVerifyCrt); err != nil { - return nil, fmt.Errorf("verifyCRL() failed: %v", err) - } - if cfg.Cache != nil { - cfg.Cache.Add(hex.EncodeToString(rawIssuer), crl) - } - return crl, nil -} - -// checkCert checks a single certificate against the CRL defined in the certificate. -// It will fetch and verify the CRL(s) defined in the root directory specified by cfg. -// If we can't load any authoritative CRL files, the status is RevocationUndetermined. -// c is the certificate to check. -// crlVerifyCrt is the group of possible certificates to verify the crl. -func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) RevocationStatus { - crl, err := fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) - if err != nil { - // We couldn't load any CRL files for the certificate, so we don't know if it's RevocationUnrevoked or not. - grpclogLogger.Warningf("getIssuerCRL(%v) err = %v", c.Issuer, err) - return RevocationUndetermined - } - revocation, err := checkCertRevocation(c, crl) - if err != nil { - grpclogLogger.Warningf("checkCertRevocation(CRL %v) failed: %v", crl.CertList.TBSCertList.Issuer, err) - // We couldn't check the CRL file for some reason, so we don't know if it's RevocationUnrevoked or not. - return RevocationUndetermined - } - // Here we've gotten a CRL that loads and verifies. - // We only handle all-reasons CRL files, so this file - // is authoritative for the certificate. - return revocation -} - -func checkCertRevocation(c *x509.Certificate, crl *CRL) (RevocationStatus, error) { - // Per section 5.3.3 we prime the certificate issuer with the CRL issuer. - // Subsequent entries use the previous entry's issuer. - rawEntryIssuer := crl.RawIssuer - - // Loop through all the revoked certificates. - for _, revCert := range crl.CertList.TBSCertList.RevokedCertificates { - // 5.3 Loop through CRL entry extensions for needed information. - for _, ext := range revCert.Extensions { - if oidCertificateIssuer.Equal(ext.Id) { - extIssuer, err := parseCertIssuerExt(ext) - if err != nil { - grpclogLogger.Info(err) - if ext.Critical { - return RevocationUndetermined, err - } - // Since this is a non-critical extension, we can skip it even though - // there was a parsing failure. - continue - } - rawEntryIssuer = extIssuer - } else if ext.Critical { - return RevocationUndetermined, fmt.Errorf("checkCertRevocation: Unhandled critical extension: %v", ext.Id) - } - } - - // If the issuer and serial number appear in the CRL, the certificate is revoked. - if bytes.Equal(c.RawIssuer, rawEntryIssuer) && c.SerialNumber.Cmp(revCert.SerialNumber) == 0 { - // CRL contains the serial, so return revoked. - return RevocationRevoked, nil - } - } - // We did not find the serial in the CRL file that was valid for the cert - // so the certificate is not revoked. - return RevocationUnrevoked, nil -} - -func parseCertIssuerExt(ext pkix.Extension) ([]byte, error) { - // 5.3.3 Certificate Issuer - // CertificateIssuer ::= GeneralNames - // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - var generalNames []asn1.RawValue - if rest, err := asn1.Unmarshal(ext.Value, &generalNames); err != nil || len(rest) != 0 { - return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) - } - - for _, generalName := range generalNames { - // GeneralName ::= CHOICE { - // otherName [0] OtherName, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER } - if generalName.Tag == tagDirectoryName { - return generalName.Bytes, nil - } - } - // Conforming CRL issuers MUST include in this extension the - // distinguished name (DN) from the issuer field of the certificate that - // corresponds to this CRL entry. - // If we couldn't get a directoryName, we can't reason about this file so cert status is - // RevocationUndetermined. - return nil, errors.New("no DN found in certificate issuer") -} - -// RFC 5280, 4.2.1.1 -type authKeyID struct { - ID []byte `asn1:"optional,tag:0"` -} - -// RFC5280, 5.2.5 -// id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } - -// IssuingDistributionPoint ::= SEQUENCE { -// distributionPoint [0] DistributionPointName OPTIONAL, -// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, -// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, -// onlySomeReasons [3] ReasonFlags OPTIONAL, -// indirectCRL [4] BOOLEAN DEFAULT FALSE, -// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } - -// -- at most one of onlyContainsUserCerts, onlyContainsCACerts, -// -- and onlyContainsAttributeCerts may be set to TRUE. -type issuingDistributionPoint struct { - DistributionPoint asn1.RawValue `asn1:"optional,tag:0"` - OnlyContainsUserCerts bool `asn1:"optional,tag:1"` - OnlyContainsCACerts bool `asn1:"optional,tag:2"` - OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"` - IndirectCRL bool `asn1:"optional,tag:4"` - OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"` -} - -// parseCRLExtensions parses the extensions for a CRL -// and checks that they're supported by the parser. -func parseCRLExtensions(c *pkix.CertificateList) (*CRL, error) { - if c == nil { - return nil, errors.New("c is nil, expected any value") - } - certList := &CRL{CertList: c} - - for _, ext := range c.TBSCertList.Extensions { - switch { - case oidDeltaCRLIndicator.Equal(ext.Id): - return nil, fmt.Errorf("delta CRLs unsupported") - - case oidAuthorityKeyIdentifier.Equal(ext.Id): - var a authKeyID - if rest, err := asn1.Unmarshal(ext.Value, &a); err != nil { - return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) - } else if len(rest) != 0 { - return nil, errors.New("trailing data after AKID extension") - } - certList.AuthorityKeyID = a.ID - - case oidIssuingDistributionPoint.Equal(ext.Id): - var dp issuingDistributionPoint - if rest, err := asn1.Unmarshal(ext.Value, &dp); err != nil { - return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) - } else if len(rest) != 0 { - return nil, errors.New("trailing data after IssuingDistributionPoint extension") - } - - if dp.OnlyContainsUserCerts || dp.OnlyContainsCACerts || dp.OnlyContainsAttributeCerts { - return nil, errors.New("CRL only contains some certificate types") - } - if dp.IndirectCRL { - return nil, errors.New("indirect CRLs unsupported") - } - if dp.OnlySomeReasons.BitLength != 0 { - return nil, errors.New("onlySomeReasons unsupported") - } - - case ext.Critical: - return nil, fmt.Errorf("unsupported critical extension: %v", ext.Id) - } - } - - if len(certList.AuthorityKeyID) == 0 { - return nil, errors.New("authority key identifier extension missing") - } - return certList, nil -} - -func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { - var parsedCRL *CRL - // 6.3.3 (a) (1) (ii) - // According to X509_LOOKUP_hash_dir the format is issuer_hash.rN where N is an increasing number. - // There are no gaps, so we break when we can't find a file. - for i := 0; ; i++ { - // Unmarshal to RDNSeqence according to http://go/godoc/crypto/x509/pkix/#Name. - var r pkix.RDNSequence - rest, err := asn1.Unmarshal(rawIssuer, &r) - if len(rest) != 0 || err != nil { - return nil, fmt.Errorf("asn1.Unmarshal(Issuer) len(rest) = %d failed: %v", len(rest), err) - } - crlPath := fmt.Sprintf("%s.r%d", filepath.Join(cfg.RootDir, x509NameHash(r)), i) - crlBytes, err := os.ReadFile(crlPath) - if err != nil { - // Break when we can't read a CRL file. - grpclogLogger.Infof("readFile: %v", err) - break - } - - crl, err := x509.ParseCRL(crlBytes) - if err != nil { - // Parsing errors for a CRL shouldn't happen so fail. - return nil, fmt.Errorf("x509.ParseCrl(%v) failed: %v", crlPath, err) - } - var certList *CRL - if certList, err = parseCRLExtensions(crl); err != nil { - grpclogLogger.Infof("fetchCRL: unsupported crl %v: %v", crlPath, err) - // Continue to find a supported CRL - continue - } - - rawCRLIssuer, err := extractCRLIssuer(crlBytes) - if err != nil { - return nil, err - } - certList.RawIssuer = rawCRLIssuer - // RFC5280, 6.3.3 (b) Verify the issuer and scope of the complete CRL. - if bytes.Equal(rawIssuer, rawCRLIssuer) { - parsedCRL = certList - // Continue to find the highest number in the .rN suffix. - continue - } - } - - if parsedCRL == nil { - return nil, fmt.Errorf("fetchCrls no CRLs found for issuer") - } - return parsedCRL, nil -} - -func verifyCRL(crl *CRL, rawIssuer []byte, chain []*x509.Certificate) error { - // RFC5280, 6.3.3 (f) Obtain and validateate the certification path for the issuer of the complete CRL - // We intentionally limit our CRLs to be signed with the same certificate path as the certificate - // so we can use the chain from the connection. - - for _, c := range chain { - // Use the key where the subject and KIDs match. - // This departs from RFC4158, 3.5.12 which states that KIDs - // cannot eliminate certificates, but RFC5280, 5.2.1 states that - // "Conforming CRL issuers MUST use the key identifier method, and MUST - // include this extension in all CRLs issued." - // So, this is much simpler than RFC4158 and should be compatible. - if bytes.Equal(c.SubjectKeyId, crl.AuthorityKeyID) && bytes.Equal(c.RawSubject, crl.RawIssuer) { - // RFC5280, 6.3.3 (g) Validate signature. - return c.CheckCRLSignature(crl.CertList) - } - } - return fmt.Errorf("verifyCRL: No certificates mached CRL issuer (%v)", crl.CertList.TBSCertList.Issuer) -} - -var crlPemPrefix = []byte("-----BEGIN X509 CRL") - -// extractCRLIssuer extracts the raw ASN.1 encoding of the CRL issuer. Due to the design of -// pkix.CertificateList and pkix.RDNSequence, it is not possible to reliably marshal the -// parsed Issuer to it's original raw encoding. -func extractCRLIssuer(crlBytes []byte) ([]byte, error) { - if bytes.HasPrefix(crlBytes, crlPemPrefix) { - block, _ := pem.Decode(crlBytes) - if block != nil && block.Type == "X509 CRL" { - crlBytes = block.Bytes - } - } - - der := cryptobyte.String(crlBytes) - var issuer cryptobyte.String - if !der.ReadASN1(&der, cbasn1.SEQUENCE) || - !der.ReadASN1(&der, cbasn1.SEQUENCE) || - !der.SkipOptionalASN1(cbasn1.INTEGER) || - !der.SkipASN1(cbasn1.SEQUENCE) || - !der.ReadASN1Element(&issuer, cbasn1.SEQUENCE) { - return nil, errors.New("extractCRLIssuer: invalid ASN.1 encoding") - } - return issuer, nil -} diff --git a/security/advancedtls/crl_deprecated_test.go b/security/advancedtls/crl_deprecated_test.go deleted file mode 100644 index e16ec7ef2599..000000000000 --- a/security/advancedtls/crl_deprecated_test.go +++ /dev/null @@ -1,775 +0,0 @@ -// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported -//go:build !go1.19 - -/* - * - * Copyright 2021 gRPC authors. - * - * 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 advancedtls - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/hex" - "encoding/pem" - "fmt" - "math/big" - "net" - "os" - "path" - "strings" - "testing" - "time" - - lru "github.com/hashicorp/golang-lru" - "google.golang.org/grpc/security/advancedtls/testdata" -) - -func TestX509NameHash(t *testing.T) { - nameTests := []struct { - in pkix.Name - out string - }{ - { - in: pkix.Name{ - Country: []string{"US"}, - Organization: []string{"Example"}, - }, - out: "9cdd41ff", - }, - { - in: pkix.Name{ - Country: []string{"us"}, - Organization: []string{"example"}, - }, - out: "9cdd41ff", - }, - { - in: pkix.Name{ - Country: []string{" us"}, - Organization: []string{"example"}, - }, - out: "9cdd41ff", - }, - { - in: pkix.Name{ - Country: []string{"US"}, - Province: []string{"California"}, - Locality: []string{"Mountain View"}, - Organization: []string{"BoringSSL"}, - }, - out: "c24414d9", - }, - { - in: pkix.Name{ - Country: []string{"US"}, - Province: []string{"California"}, - Locality: []string{"Mountain View"}, - Organization: []string{"BoringSSL"}, - }, - out: "c24414d9", - }, - { - in: pkix.Name{ - SerialNumber: "87f4514475ba0a2b", - }, - out: "9dc713cd", - }, - { - in: pkix.Name{ - Country: []string{"US"}, - Province: []string{"California"}, - Locality: []string{"Mountain View"}, - Organization: []string{"Google LLC"}, - OrganizationalUnit: []string{"Production", "campus-sln"}, - CommonName: "Root CA (2021-02-02T07:30:36-08:00)", - }, - out: "0b35a562", - }, - { - in: pkix.Name{ - ExtraNames: []pkix.AttributeTypeAndValue{ - {Type: asn1.ObjectIdentifier{5, 5, 5, 5}, Value: "aaaa"}, - }, - }, - out: "eea339da", - }, - } - for _, tt := range nameTests { - t.Run(tt.in.String(), func(t *testing.T) { - h := x509NameHash(tt.in.ToRDNSequence()) - if h != tt.out { - t.Errorf("x509NameHash(%v): Got %v wanted %v", tt.in, h, tt.out) - } - }) - } -} - -func TestUnsupportedCRLs(t *testing.T) { - crlBytesSomeReasons := []byte(`-----BEGIN X509 CRL----- -MIIEeDCCA2ACAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVVMxHjAcBgNV -BAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczETMBEGA1UEAxMKR1RTIENBIDFPMRcN -MjEwNDI2MTI1OTQxWhcNMjEwNTA2MTE1OTQwWjCCAn0wIgIRAPOOG3L4VLC7CAAA -AABxQgEXDTIxMDQxOTEyMTgxOFowIQIQUK0UwBZkVdQIAAAAAHFCBRcNMjEwNDE5 -MTIxODE4WjAhAhBRIXBJaKoQkQgAAAAAcULHFw0yMTA0MjAxMjE4MTdaMCICEQCv -qQWUq5UxmQgAAAAAcULMFw0yMTA0MjAxMjE4MTdaMCICEQDdv5k1kKwKTQgAAAAA -cUOQFw0yMTA0MjExMjE4MTZaMCICEQDGIEfR8N9sEAgAAAAAcUOWFw0yMTA0MjEx -MjE4MThaMCECEBHgbLXlj5yUCAAAAABxQ/IXDTIxMDQyMTIzMDAyNlowIQIQE1wT -2GGYqKwIAAAAAHFD7xcNMjEwNDIxMjMwMDI5WjAiAhEAo/bSyDjpVtsIAAAAAHFE -txcNMjEwNDIyMjMwMDI3WjAhAhARdCrSrHE0dAgAAAAAcUS/Fw0yMTA0MjIyMzAw -MjhaMCECEHONohfWn3wwCAAAAABxRX8XDTIxMDQyMzIzMDAyOVowIgIRAOYkiUPA -os4vCAAAAABxRYgXDTIxMDQyMzIzMDAyOFowIQIQRNTow5Eg2gEIAAAAAHFGShcN -MjEwNDI0MjMwMDI2WjAhAhBX32dH4/WQ6AgAAAAAcUZNFw0yMTA0MjQyMzAwMjZa -MCICEQDHnUM1vsaP/wgAAAAAcUcQFw0yMTA0MjUyMzAwMjZaMCECEEm5rvmL8sj6 -CAAAAABxRxQXDTIxMDQyNTIzMDAyN1owIQIQW16OQs4YQYkIAAAAAHFIABcNMjEw -NDI2MTI1NDA4WjAhAhAhSohpYsJtDQgAAAAAcUgEFw0yMTA0MjYxMjU0MDlaoGkw -ZzAfBgNVHSMEGDAWgBSY0fhuEOvPm+xgnxiQG6DrfQn9KzALBgNVHRQEBAICBngw -NwYDVR0cAQH/BC0wK6AmoCSGImh0dHA6Ly9jcmwucGtpLmdvb2cvR1RTMU8xY29y -ZS5jcmyBAf8wDQYJKoZIhvcNAQELBQADggEBADPBXbxVxMJ1HC7btXExRUpJHUlU -YbeCZGx6zj5F8pkopbmpV7cpewwhm848Fx4VaFFppZQZd92O08daEC6aEqoug4qF -z6ZrOLzhuKfpW8E93JjgL91v0FYN7iOcT7+ERKCwVEwEkuxszxs7ggW6OJYJNvHh -priIdmcPoiQ3ZrIRH0vE3BfUcNXnKFGATWuDkiRI0I4A5P7NiOf+lAuGZet3/eom -0chgts6sdau10GfeUpHUd4f8e93cS/QeLeG16z7LC8vRLstU3m3vrknpZbdGqSia -97w66mqcnQh9V0swZiEnVLmLufaiuDZJ+6nUzSvLqBlb/ei3T/tKV0BoKJA= ------END X509 CRL-----`) - - crlBytesIndirect := []byte(`-----BEGIN X509 CRL----- -MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU -ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg -Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 -MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG -EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 -MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 -MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV -BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j -BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ -BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG -SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS -TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG -NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq -XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF -6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 -qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 ------END X509 CRL-----`) - - var tests = []struct { - desc string - in []byte - }{ - { - desc: "some reasons", - in: crlBytesSomeReasons, - }, - { - desc: "indirect", - in: crlBytesIndirect, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - crl, err := x509.ParseCRL(tt.in) - if err != nil { - t.Fatal(err) - } - if _, err := parseCRLExtensions(crl); err == nil { - t.Error("expected error got ok") - } - }) - } -} - -func TestCheckCertRevocation(t *testing.T) { - dummyCrlFile := []byte(`-----BEGIN X509 CRL----- -MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU -ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg -Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 -MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG -EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 -MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 -MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV -BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j -BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ -BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG -SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS -TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG -NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq -XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF -6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 -qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 ------END X509 CRL-----`) - crl, err := x509.ParseCRL(dummyCrlFile) - if err != nil { - t.Fatalf("x509.ParseCRL(dummyCrlFile) failed: %v", err) - } - crlExt := &CRL{certList: crl} - var crlIssuer pkix.Name - crlIssuer.FillFromRDNSequence(&crl.TBSCertList.Issuer) - - var revocationTests = []struct { - desc string - in x509.Certificate - revoked RevocationStatus - }{ - { - desc: "Single revoked", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test1", - }, - SerialNumber: big.NewInt(2), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationRevoked, - }, - { - desc: "Revoked no entry issuer", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test1", - }, - SerialNumber: big.NewInt(3), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationRevoked, - }, - { - desc: "Revoked new entry issuer", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test2", - }, - SerialNumber: big.NewInt(4), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationRevoked, - }, - { - desc: "Single unrevoked", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test2", - }, - SerialNumber: big.NewInt(1), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationUnrevoked, - }, - { - desc: "Single unrevoked Issuer", - in: x509.Certificate{ - Issuer: crlIssuer, - SerialNumber: big.NewInt(2), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationUnrevoked, - }, - } - - for _, tt := range revocationTests { - rawIssuer, err := asn1.Marshal(tt.in.Issuer.ToRDNSequence()) - if err != nil { - t.Fatalf("asn1.Marshal(%v) failed: %v", tt.in.Issuer.ToRDNSequence(), err) - } - tt.in.RawIssuer = rawIssuer - t.Run(tt.desc, func(t *testing.T) { - rev, err := checkCertRevocation(&tt.in, crlExt) - if err != nil { - t.Errorf("checkCertRevocation(%v) err = %v", tt.in.Issuer, err) - } else if rev != tt.revoked { - t.Errorf("checkCertRevocation(%v(%v)) returned %v wanted %v", - tt.in.Issuer, tt.in.SerialNumber, rev, tt.revoked) - } - }) - } -} - -func makeChain(t *testing.T, name string) []*x509.Certificate { - t.Helper() - - certChain := make([]*x509.Certificate, 0) - - rest, err := os.ReadFile(name) - if err != nil { - t.Fatalf("os.ReadFile(%v) failed %v", name, err) - } - for len(rest) > 0 { - var block *pem.Block - block, rest = pem.Decode(rest) - c, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Fatalf("ParseCertificate error %v", err) - } - t.Logf("Parsed Cert sub = %v iss = %v", c.Subject, c.Issuer) - certChain = append(certChain, c) - } - return certChain -} - -func loadCRL(t *testing.T, path string) *CRL { - b, err := os.ReadFile(path) - if err != nil { - t.Fatalf("readFile(%v) failed err = %v", path, err) - } - crl, err := x509.ParseCRL(b) - if err != nil { - t.Fatalf("ParseCrl(%v) failed err = %v", path, err) - } - crlExt, err := parseCRLExtensions(crl) - if err != nil { - t.Fatalf("parseCRLExtensions(%v) failed err = %v", path, err) - } - crlExt.rawIssuer, err = extractCRLIssuer(b) - if err != nil { - t.Fatalf("extractCRLIssuer(%v) failed err= %v", path, err) - } - return crlExt -} - -func TestCachedCRL(t *testing.T) { - cache, err := lru.New(5) - if err != nil { - t.Fatalf("lru.New: err = %v", err) - } - - tests := []struct { - desc string - val interface{} - ok bool - }{ - { - desc: "Valid", - val: &CRL{ - certList: &pkix.CertificateList{ - TBSCertList: pkix.TBSCertificateList{ - NextUpdate: time.Now().Add(time.Hour), - }, - }}, - ok: true, - }, - { - desc: "Expired", - val: &CRL{ - certList: &pkix.CertificateList{ - TBSCertList: pkix.TBSCertificateList{ - NextUpdate: time.Now().Add(-time.Hour), - }, - }}, - ok: false, - }, - { - desc: "Wrong Type", - val: "string", - ok: false, - }, - { - desc: "Empty", - val: nil, - ok: false, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if tt.val != nil { - cache.Add(hex.EncodeToString([]byte(tt.desc)), tt.val) - } - _, ok := cachedCrl([]byte(tt.desc), cache) - if tt.ok != ok { - t.Errorf("Cache ok error expected %v vs %v", tt.ok, ok) - } - }) - } -} - -func TestGetIssuerCRLCache(t *testing.T) { - cache, err := lru.New(5) - if err != nil { - t.Fatalf("lru.New: err = %v", err) - } - - tests := []struct { - desc string - rawIssuer []byte - certs []*x509.Certificate - }{ - { - desc: "Valid", - rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - }, - { - desc: "Unverified", - rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, - }, - { - desc: "Not Found", - rawIssuer: []byte("not_found"), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - cache.Purge() - _, err := fetchIssuerCRL(tt.rawIssuer, tt.certs, RevocationConfig{ - RootDir: testdata.Path("."), - Cache: cache, - }) - if err == nil && cache.Len() == 0 { - t.Error("Verified CRL not added to cache") - } - if err != nil && cache.Len() != 0 { - t.Error("Unverified CRL added to cache") - } - }) - } -} - -func TestVerifyCrl(t *testing.T) { - tampered := loadCRL(t, testdata.Path("crl/1.crl")) - // Change the signature so it won't verify - tampered.certList.SignatureValue.Bytes[0]++ - - verifyTests := []struct { - desc string - crl *CRL - certs []*x509.Certificate - cert *x509.Certificate - errWant string - }{ - { - desc: "Pass intermediate", - crl: loadCRL(t, testdata.Path("crl/1.crl")), - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], - errWant: "", - }, - { - desc: "Pass leaf", - crl: loadCRL(t, testdata.Path("crl/2.crl")), - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[2], - errWant: "", - }, - { - desc: "Fail wrong cert chain", - crl: loadCRL(t, testdata.Path("crl/3.crl")), - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/revokedInt.pem"))[1], - errWant: "No certificates mached", - }, - { - desc: "Fail no certs", - crl: loadCRL(t, testdata.Path("crl/1.crl")), - certs: []*x509.Certificate{}, - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], - errWant: "No certificates mached", - }, - { - desc: "Fail Tampered signature", - crl: tampered, - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], - errWant: "verification failure", - }, - } - - for _, tt := range verifyTests { - t.Run(tt.desc, func(t *testing.T) { - err := verifyCRL(tt.crl, tt.cert.RawIssuer, tt.certs) - switch { - case tt.errWant == "" && err != nil: - t.Errorf("Valid CRL did not verify err = %v", err) - case tt.errWant != "" && err == nil: - t.Error("Invalid CRL verified") - case tt.errWant != "" && !strings.Contains(err.Error(), tt.errWant): - t.Errorf("fetchIssuerCRL(_, %v, %v, _) = %v; want Contains(%v)", tt.cert.RawIssuer, tt.certs, err, tt.errWant) - } - }) - } -} - -func TestRevokedCert(t *testing.T) { - revokedIntChain := makeChain(t, testdata.Path("crl/revokedInt.pem")) - revokedLeafChain := makeChain(t, testdata.Path("crl/revokedLeaf.pem")) - validChain := makeChain(t, testdata.Path("crl/unrevoked.pem")) - cache, err := lru.New(5) - if err != nil { - t.Fatalf("lru.New: err = %v", err) - } - - var revocationTests = []struct { - desc string - in tls.ConnectionState - revoked bool - allowUndetermined bool - }{ - { - desc: "Single unrevoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain}}, - revoked: false, - }, - { - desc: "Single revoked intermediate", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedIntChain}}, - revoked: true, - }, - { - desc: "Single revoked leaf", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain}}, - revoked: true, - }, - { - desc: "Multi one revoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, revokedLeafChain}}, - revoked: false, - }, - { - desc: "Multi revoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain, revokedIntChain}}, - revoked: true, - }, - { - desc: "Multi unrevoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, validChain}}, - revoked: false, - }, - { - desc: "Undetermined revoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ - {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, - }}, - revoked: true, - }, - { - desc: "Undetermined allowed", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ - {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, - }}, - revoked: false, - allowUndetermined: true, - }, - } - - for _, tt := range revocationTests { - t.Run(tt.desc, func(t *testing.T) { - err := CheckRevocation(tt.in, RevocationConfig{ - RootDir: testdata.Path("crl"), - AllowUndetermined: tt.allowUndetermined, - Cache: cache, - }) - t.Logf("CheckRevocation err = %v", err) - if tt.revoked && err == nil { - t.Error("Revoked certificate chain was allowed") - } else if !tt.revoked && err != nil { - t.Error("Unrevoked certificate not allowed") - } - }) - } -} - -func setupTLSConn(t *testing.T) (net.Listener, *x509.Certificate, *ecdsa.PrivateKey) { - t.Helper() - templ := x509.Certificate{ - SerialNumber: big.NewInt(5), - BasicConstraintsValid: true, - NotBefore: time.Now().Add(-time.Hour), - NotAfter: time.Now().Add(time.Hour), - IsCA: true, - Subject: pkix.Name{CommonName: "test-cert"}, - KeyUsage: x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - CRLDistributionPoints: []string{"http://static.corp.google.com/crl/campus-sln/borg"}, - } - - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatalf("ecdsa.GenerateKey failed err = %v", err) - } - rawCert, err := x509.CreateCertificate(rand.Reader, &templ, &templ, key.Public(), key) - if err != nil { - t.Fatalf("x509.CreateCertificate failed err = %v", err) - } - cert, err := x509.ParseCertificate(rawCert) - if err != nil { - t.Fatalf("x509.ParseCertificate failed err = %v", err) - } - - srvCfg := tls.Config{ - Certificates: []tls.Certificate{ - { - Certificate: [][]byte{cert.Raw}, - PrivateKey: key, - }, - }, - } - l, err := tls.Listen("tcp6", "[::1]:0", &srvCfg) - if err != nil { - t.Fatalf("tls.Listen failed err = %v", err) - } - return l, cert, key -} - -// TestVerifyConnection will setup a client/server connection and check revocation in the real TLS dialer -func TestVerifyConnection(t *testing.T) { - lis, cert, key := setupTLSConn(t) - defer func() { - lis.Close() - }() - - var handshakeTests = []struct { - desc string - revoked []pkix.RevokedCertificate - success bool - }{ - { - desc: "Empty CRL", - revoked: []pkix.RevokedCertificate{}, - success: true, - }, - { - desc: "Revoked Cert", - revoked: []pkix.RevokedCertificate{ - { - SerialNumber: cert.SerialNumber, - RevocationTime: time.Now(), - }, - }, - success: false, - }, - } - for _, tt := range handshakeTests { - t.Run(tt.desc, func(t *testing.T) { - // Accept one connection. - go func() { - conn, err := lis.Accept() - if err != nil { - t.Errorf("tls.Accept failed err = %v", err) - } else { - conn.Write([]byte("Hello, World!")) - conn.Close() - } - }() - - dir, err := os.MkdirTemp("", "crl_dir") - if err != nil { - t.Fatalf("os.MkdirTemp failed err = %v", err) - } - defer os.RemoveAll(dir) - - crl, err := cert.CreateCRL(rand.Reader, key, tt.revoked, time.Now(), time.Now().Add(time.Hour)) - if err != nil { - t.Fatalf("templ.CreateCRL failed err = %v", err) - } - - err = os.WriteFile(path.Join(dir, fmt.Sprintf("%s.r0", x509NameHash(cert.Subject.ToRDNSequence()))), crl, 0777) - if err != nil { - t.Fatalf("os.WriteFile failed err = %v", err) - } - - cp := x509.NewCertPool() - cp.AddCert(cert) - cliCfg := tls.Config{ - RootCAs: cp, - VerifyConnection: func(cs tls.ConnectionState) error { - return CheckRevocation(cs, RevocationConfig{RootDir: dir}) - }, - } - conn, err := tls.Dial(lis.Addr().Network(), lis.Addr().String(), &cliCfg) - t.Logf("tls.Dial err = %v", err) - if tt.success && err != nil { - t.Errorf("Expected success got err = %v", err) - } - if !tt.success && err == nil { - t.Error("Expected error, but got success") - } - if err == nil { - conn.Close() - } - }) - } -} - -func TestIssuerNonPrintableString(t *testing.T) { - rawIssuer, err := hex.DecodeString("300c310a300806022a030c023a29") - if err != nil { - t.Fatalf("failed to decode issuer: %s", err) - } - _, err = fetchCRL(rawIssuer, RevocationConfig{RootDir: testdata.Path("crl")}) - if err != nil { - t.Fatalf("fetchCRL failed: %s", err) - } -} - -// TestCRLCacheExpirationReloading tests the basic expiration and reloading of a -// cached CRL. The setup places an empty CRL in the cache, and a corresponding -// CRL with a revocation in the CRL directory. We then validate the certificate -// to verify that the certificate is not revoked. Then, we modify the -// NextUpdate time to be in the past so that when we next check for revocation, -// the existing cache entry should be seen as expired, and the CRL in the -// directory showing `revokedInt.pem` as revoked will be loaded, resulting in -// the check returning `RevocationRevoked`. -func TestCRLCacheExpirationReloading(t *testing.T) { - cache, err := lru.New(5) - if err != nil { - t.Fatalf("Creating cache failed") - } - - var certs = makeChain(t, testdata.Path("crl/revokedInt.pem")) - // Certs[1] has the same issuer as the revoked cert - rawIssuer := certs[1].RawIssuer - - // `3.crl`` revokes `revokedInt.pem` - crl := loadCRL(t, testdata.Path("crl/3.crl")) - // Modify the crl so that the cert is NOT revoked and add it to the cache - crl.certList.TBSCertList.RevokedCertificates = nil - crl.certList.TBSCertList.NextUpdate = time.Now().Add(time.Hour) - cache.Add(hex.EncodeToString(rawIssuer), crl) - var cfg = RevocationConfig{RootDir: testdata.Path("crl"), Cache: cache} - revocationStatus := checkChain(certs, cfg) - if revocationStatus != RevocationUnrevoked { - t.Fatalf("Certificate check should be RevocationUnrevoked, was %v", revocationStatus) - } - - // Modify the entry in the cache so that the cache will be refreshed - crl.certList.TBSCertList.NextUpdate = time.Now() - cache.Add(hex.EncodeToString(rawIssuer), crl) - - revocationStatus = checkChain(certs, cfg) - if revocationStatus != RevocationRevoked { - t.Fatalf("A certificate should have been `RevocationRevoked` but was %v", revocationStatus) - } -} diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 366fe3af6d3d..4f072a146d6b 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -189,7 +189,7 @@ func TestFileWatcherCRLProvider(t *testing.T) { } // TestFileWatcherCRLProviderDirectoryScan tests how FileWatcherCRLProvider -// handles different contents of Options.CRLDirectory +// handles different contents of Options.CRLDirectory. // We update the content with various (correct and incorrect) CRL files and // check if in-memory storage was properly updated. Please note that the same // instance of FileWatcherCRLProvider is used for the whole test so test cases From 56437603a432b36deee8657bb947f49f10b8767b Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Wed, 11 Oct 2023 17:58:15 +0000 Subject: [PATCH 36/62] Restoring deprecated crl files --- security/advancedtls/crl_deprecated.go | 521 +++++++++++++ security/advancedtls/crl_deprecated_test.go | 775 ++++++++++++++++++++ 2 files changed, 1296 insertions(+) create mode 100644 security/advancedtls/crl_deprecated.go create mode 100644 security/advancedtls/crl_deprecated_test.go diff --git a/security/advancedtls/crl_deprecated.go b/security/advancedtls/crl_deprecated.go new file mode 100644 index 000000000000..cd7b3d1228ad --- /dev/null +++ b/security/advancedtls/crl_deprecated.go @@ -0,0 +1,521 @@ +// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported +//go:build !go1.19 + +/* + * + * Copyright 2021 gRPC authors. + * + * 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 advancedtls + +import ( + "bytes" + "crypto/sha1" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/binary" + "encoding/hex" + "encoding/pem" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "golang.org/x/crypto/cryptobyte" + cbasn1 "golang.org/x/crypto/cryptobyte/asn1" + "google.golang.org/grpc/grpclog" +) + +var grpclogLogger = grpclog.Component("advancedtls") + +// Cache is an interface to cache CRL files. +// The cache implementation must be concurrency safe. +// A fixed size lru cache from golang-lru is recommended. +type Cache interface { + // Add adds a value to the cache. + Add(key, value interface{}) bool + // Get looks up a key's value from the cache. + Get(key interface{}) (value interface{}, ok bool) +} + +// RevocationConfig contains options for CRL lookup. +type RevocationConfig struct { + // RootDir is the directory to search for CRL files. + // Directory format must match OpenSSL X509_LOOKUP_hash_dir(3). + RootDir string + // AllowUndetermined controls if certificate chains with RevocationUndetermined + // revocation status are allowed to complete. + AllowUndetermined bool + // Cache will store CRL files if not nil, otherwise files are reloaded for every lookup. + Cache Cache +} + +// RevocationStatus is the revocation status for a certificate or chain. +type RevocationStatus int + +const ( + // RevocationUndetermined means we couldn't find or verify a CRL for the cert. + RevocationUndetermined RevocationStatus = iota + // RevocationUnrevoked means we found the CRL for the cert and the cert is not revoked. + RevocationUnrevoked + // RevocationRevoked means we found the CRL and the cert is revoked. + RevocationRevoked +) + +func (s RevocationStatus) String() string { + return [...]string{"RevocationUndetermined", "RevocationUnrevoked", "RevocationRevoked"}[s] +} + +// CRL contains a pkix.CertificateList and parsed +// extensions that aren't provided by the golang CRL parser. +type CRL struct { + CertList *pkix.CertificateList + // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. + AuthorityKeyID []byte + RawIssuer []byte +} + +const tagDirectoryName = 4 + +var ( + // RFC5280, 5.2.4 id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } + oidDeltaCRLIndicator = asn1.ObjectIdentifier{2, 5, 29, 27} + // RFC5280, 5.2.5 id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } + oidIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28} + // RFC5280, 5.3.3 id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } + oidCertificateIssuer = asn1.ObjectIdentifier{2, 5, 29, 29} + // RFC5290, 4.2.1.1 id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } + oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35} +) + +// x509NameHash implements the OpenSSL X509_NAME_hash function for hashed directory lookups. +// +// NOTE: due to the behavior of asn1.Marshal, if the original encoding of the RDN sequence +// contains strings which do not use the ASN.1 PrintableString type, the name will not be +// re-encoded using those types, resulting in a hash which does not match that produced +// by OpenSSL. +func x509NameHash(r pkix.RDNSequence) string { + var canonBytes []byte + // First, canonicalize all the strings. + for _, rdnSet := range r { + for i, rdn := range rdnSet { + value, ok := rdn.Value.(string) + if !ok { + continue + } + // OpenSSL trims all whitespace, does a tolower, and removes extra spaces between words. + // Implemented in x509_name_canon in OpenSSL + canonStr := strings.Join(strings.Fields( + strings.TrimSpace(strings.ToLower(value))), " ") + // Then it changes everything to UTF8 strings + rdnSet[i].Value = asn1.RawValue{Tag: asn1.TagUTF8String, Bytes: []byte(canonStr)} + + } + } + + // Finally, OpenSSL drops the initial sequence tag + // so we marshal all the RDNs separately instead of as a group. + for _, canonRdn := range r { + b, err := asn1.Marshal(canonRdn) + if err != nil { + continue + } + canonBytes = append(canonBytes, b...) + } + + issuerHash := sha1.Sum(canonBytes) + // Openssl takes the first 4 bytes and encodes them as a little endian + // uint32 and then uses the hex to make the file name. + // In C++, this would be: + // (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | + // ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) + // ) & 0xffffffffL; + fileHash := binary.LittleEndian.Uint32(issuerHash[0:4]) + return fmt.Sprintf("%08x", fileHash) +} + +// CheckRevocation checks the connection for revoked certificates based on RFC5280. +// This implementation has the following major limitations: +// - Indirect CRL files are not supported. +// - CRL loading is only supported from directories in the X509_LOOKUP_hash_dir format. +// - OnlySomeReasons is not supported. +// - Delta CRL files are not supported. +// - Certificate CRLDistributionPoint must be URLs, but are then ignored and converted into a file path. +// - CRL checks are done after path building, which goes against RFC4158. +func CheckRevocation(conn tls.ConnectionState, cfg RevocationConfig) error { + return CheckChainRevocation(conn.VerifiedChains, cfg) +} + +// CheckChainRevocation checks the verified certificate chain +// for revoked certificates based on RFC5280. +func CheckChainRevocation(verifiedChains [][]*x509.Certificate, cfg RevocationConfig) error { + // Iterate the verified chains looking for one that is RevocationUnrevoked. + // A single RevocationUnrevoked chain is enough to allow the connection, and a single RevocationRevoked + // chain does not mean the connection should fail. + count := make(map[RevocationStatus]int) + for _, chain := range verifiedChains { + switch checkChain(chain, cfg) { + case RevocationUnrevoked: + // If any chain is RevocationUnrevoked then return no error. + return nil + case RevocationRevoked: + // If this chain is revoked, keep looking for another chain. + count[RevocationRevoked]++ + continue + case RevocationUndetermined: + if cfg.AllowUndetermined { + return nil + } + count[RevocationUndetermined]++ + continue + } + } + return fmt.Errorf("no unrevoked chains found: %v", count) +} + +// checkChain will determine and check all certificates in chain against the CRL +// defined in the certificate with the following rules: +// 1. If any certificate is RevocationRevoked, return RevocationRevoked. +// 2. If any certificate is RevocationUndetermined, return RevocationUndetermined. +// 3. If all certificates are RevocationUnrevoked, return RevocationUnrevoked. +func checkChain(chain []*x509.Certificate, cfg RevocationConfig) RevocationStatus { + chainStatus := RevocationUnrevoked + for _, c := range chain { + switch checkCert(c, chain, cfg) { + case RevocationRevoked: + // Easy case, if a cert in the chain is revoked, the chain is revoked. + return RevocationRevoked + case RevocationUndetermined: + // If we couldn't find the revocation status for a cert, the chain is at best RevocationUndetermined + // keep looking to see if we find a cert in the chain that's RevocationRevoked, + // but return RevocationUndetermined at a minimum. + chainStatus = RevocationUndetermined + case RevocationUnrevoked: + // Continue iterating up the cert chain. + continue + } + } + return chainStatus +} + +func cachedCrl(rawIssuer []byte, cache Cache) (*CRL, bool) { + val, ok := cache.Get(hex.EncodeToString(rawIssuer)) + if !ok { + return nil, false + } + crl, ok := val.(*CRL) + if !ok { + return nil, false + } + // If the CRL is expired, force a reload. + if crl.CertList.HasExpired(time.Now()) { + return nil, false + } + return crl, true +} + +// fetchIssuerCRL fetches and verifies the CRL for rawIssuer from disk or cache if configured in cfg. +func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*CRL, error) { + if cfg.Cache != nil { + if crl, ok := cachedCrl(rawIssuer, cfg.Cache); ok { + return crl, nil + } + } + + crl, err := fetchCRL(rawIssuer, cfg) + if err != nil { + return nil, fmt.Errorf("fetchCRL() failed: %v", err) + } + + if err := verifyCRL(crl, rawIssuer, crlVerifyCrt); err != nil { + return nil, fmt.Errorf("verifyCRL() failed: %v", err) + } + if cfg.Cache != nil { + cfg.Cache.Add(hex.EncodeToString(rawIssuer), crl) + } + return crl, nil +} + +// checkCert checks a single certificate against the CRL defined in the certificate. +// It will fetch and verify the CRL(s) defined in the root directory specified by cfg. +// If we can't load any authoritative CRL files, the status is RevocationUndetermined. +// c is the certificate to check. +// crlVerifyCrt is the group of possible certificates to verify the crl. +func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) RevocationStatus { + crl, err := fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) + if err != nil { + // We couldn't load any CRL files for the certificate, so we don't know if it's RevocationUnrevoked or not. + grpclogLogger.Warningf("getIssuerCRL(%v) err = %v", c.Issuer, err) + return RevocationUndetermined + } + revocation, err := checkCertRevocation(c, crl) + if err != nil { + grpclogLogger.Warningf("checkCertRevocation(CRL %v) failed: %v", crl.CertList.TBSCertList.Issuer, err) + // We couldn't check the CRL file for some reason, so we don't know if it's RevocationUnrevoked or not. + return RevocationUndetermined + } + // Here we've gotten a CRL that loads and verifies. + // We only handle all-reasons CRL files, so this file + // is authoritative for the certificate. + return revocation +} + +func checkCertRevocation(c *x509.Certificate, crl *CRL) (RevocationStatus, error) { + // Per section 5.3.3 we prime the certificate issuer with the CRL issuer. + // Subsequent entries use the previous entry's issuer. + rawEntryIssuer := crl.RawIssuer + + // Loop through all the revoked certificates. + for _, revCert := range crl.CertList.TBSCertList.RevokedCertificates { + // 5.3 Loop through CRL entry extensions for needed information. + for _, ext := range revCert.Extensions { + if oidCertificateIssuer.Equal(ext.Id) { + extIssuer, err := parseCertIssuerExt(ext) + if err != nil { + grpclogLogger.Info(err) + if ext.Critical { + return RevocationUndetermined, err + } + // Since this is a non-critical extension, we can skip it even though + // there was a parsing failure. + continue + } + rawEntryIssuer = extIssuer + } else if ext.Critical { + return RevocationUndetermined, fmt.Errorf("checkCertRevocation: Unhandled critical extension: %v", ext.Id) + } + } + + // If the issuer and serial number appear in the CRL, the certificate is revoked. + if bytes.Equal(c.RawIssuer, rawEntryIssuer) && c.SerialNumber.Cmp(revCert.SerialNumber) == 0 { + // CRL contains the serial, so return revoked. + return RevocationRevoked, nil + } + } + // We did not find the serial in the CRL file that was valid for the cert + // so the certificate is not revoked. + return RevocationUnrevoked, nil +} + +func parseCertIssuerExt(ext pkix.Extension) ([]byte, error) { + // 5.3.3 Certificate Issuer + // CertificateIssuer ::= GeneralNames + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + var generalNames []asn1.RawValue + if rest, err := asn1.Unmarshal(ext.Value, &generalNames); err != nil || len(rest) != 0 { + return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) + } + + for _, generalName := range generalNames { + // GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER } + if generalName.Tag == tagDirectoryName { + return generalName.Bytes, nil + } + } + // Conforming CRL issuers MUST include in this extension the + // distinguished name (DN) from the issuer field of the certificate that + // corresponds to this CRL entry. + // If we couldn't get a directoryName, we can't reason about this file so cert status is + // RevocationUndetermined. + return nil, errors.New("no DN found in certificate issuer") +} + +// RFC 5280, 4.2.1.1 +type authKeyID struct { + ID []byte `asn1:"optional,tag:0"` +} + +// RFC5280, 5.2.5 +// id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } + +// IssuingDistributionPoint ::= SEQUENCE { +// distributionPoint [0] DistributionPointName OPTIONAL, +// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, +// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, +// onlySomeReasons [3] ReasonFlags OPTIONAL, +// indirectCRL [4] BOOLEAN DEFAULT FALSE, +// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } + +// -- at most one of onlyContainsUserCerts, onlyContainsCACerts, +// -- and onlyContainsAttributeCerts may be set to TRUE. +type issuingDistributionPoint struct { + DistributionPoint asn1.RawValue `asn1:"optional,tag:0"` + OnlyContainsUserCerts bool `asn1:"optional,tag:1"` + OnlyContainsCACerts bool `asn1:"optional,tag:2"` + OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"` + IndirectCRL bool `asn1:"optional,tag:4"` + OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"` +} + +// parseCRLExtensions parses the extensions for a CRL +// and checks that they're supported by the parser. +func parseCRLExtensions(c *pkix.CertificateList) (*CRL, error) { + if c == nil { + return nil, errors.New("c is nil, expected any value") + } + certList := &CRL{CertList: c} + + for _, ext := range c.TBSCertList.Extensions { + switch { + case oidDeltaCRLIndicator.Equal(ext.Id): + return nil, fmt.Errorf("delta CRLs unsupported") + + case oidAuthorityKeyIdentifier.Equal(ext.Id): + var a authKeyID + if rest, err := asn1.Unmarshal(ext.Value, &a); err != nil { + return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) + } else if len(rest) != 0 { + return nil, errors.New("trailing data after AKID extension") + } + certList.AuthorityKeyID = a.ID + + case oidIssuingDistributionPoint.Equal(ext.Id): + var dp issuingDistributionPoint + if rest, err := asn1.Unmarshal(ext.Value, &dp); err != nil { + return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) + } else if len(rest) != 0 { + return nil, errors.New("trailing data after IssuingDistributionPoint extension") + } + + if dp.OnlyContainsUserCerts || dp.OnlyContainsCACerts || dp.OnlyContainsAttributeCerts { + return nil, errors.New("CRL only contains some certificate types") + } + if dp.IndirectCRL { + return nil, errors.New("indirect CRLs unsupported") + } + if dp.OnlySomeReasons.BitLength != 0 { + return nil, errors.New("onlySomeReasons unsupported") + } + + case ext.Critical: + return nil, fmt.Errorf("unsupported critical extension: %v", ext.Id) + } + } + + if len(certList.AuthorityKeyID) == 0 { + return nil, errors.New("authority key identifier extension missing") + } + return certList, nil +} + +func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { + var parsedCRL *CRL + // 6.3.3 (a) (1) (ii) + // According to X509_LOOKUP_hash_dir the format is issuer_hash.rN where N is an increasing number. + // There are no gaps, so we break when we can't find a file. + for i := 0; ; i++ { + // Unmarshal to RDNSeqence according to http://go/godoc/crypto/x509/pkix/#Name. + var r pkix.RDNSequence + rest, err := asn1.Unmarshal(rawIssuer, &r) + if len(rest) != 0 || err != nil { + return nil, fmt.Errorf("asn1.Unmarshal(Issuer) len(rest) = %d failed: %v", len(rest), err) + } + crlPath := fmt.Sprintf("%s.r%d", filepath.Join(cfg.RootDir, x509NameHash(r)), i) + crlBytes, err := os.ReadFile(crlPath) + if err != nil { + // Break when we can't read a CRL file. + grpclogLogger.Infof("readFile: %v", err) + break + } + + crl, err := x509.ParseCRL(crlBytes) + if err != nil { + // Parsing errors for a CRL shouldn't happen so fail. + return nil, fmt.Errorf("x509.ParseCrl(%v) failed: %v", crlPath, err) + } + var certList *CRL + if certList, err = parseCRLExtensions(crl); err != nil { + grpclogLogger.Infof("fetchCRL: unsupported crl %v: %v", crlPath, err) + // Continue to find a supported CRL + continue + } + + rawCRLIssuer, err := extractCRLIssuer(crlBytes) + if err != nil { + return nil, err + } + certList.RawIssuer = rawCRLIssuer + // RFC5280, 6.3.3 (b) Verify the issuer and scope of the complete CRL. + if bytes.Equal(rawIssuer, rawCRLIssuer) { + parsedCRL = certList + // Continue to find the highest number in the .rN suffix. + continue + } + } + + if parsedCRL == nil { + return nil, fmt.Errorf("fetchCrls no CRLs found for issuer") + } + return parsedCRL, nil +} + +func verifyCRL(crl *CRL, rawIssuer []byte, chain []*x509.Certificate) error { + // RFC5280, 6.3.3 (f) Obtain and validateate the certification path for the issuer of the complete CRL + // We intentionally limit our CRLs to be signed with the same certificate path as the certificate + // so we can use the chain from the connection. + + for _, c := range chain { + // Use the key where the subject and KIDs match. + // This departs from RFC4158, 3.5.12 which states that KIDs + // cannot eliminate certificates, but RFC5280, 5.2.1 states that + // "Conforming CRL issuers MUST use the key identifier method, and MUST + // include this extension in all CRLs issued." + // So, this is much simpler than RFC4158 and should be compatible. + if bytes.Equal(c.SubjectKeyId, crl.AuthorityKeyID) && bytes.Equal(c.RawSubject, crl.RawIssuer) { + // RFC5280, 6.3.3 (g) Validate signature. + return c.CheckCRLSignature(crl.CertList) + } + } + return fmt.Errorf("verifyCRL: No certificates mached CRL issuer (%v)", crl.CertList.TBSCertList.Issuer) +} + +var crlPemPrefix = []byte("-----BEGIN X509 CRL") + +// extractCRLIssuer extracts the raw ASN.1 encoding of the CRL issuer. Due to the design of +// pkix.CertificateList and pkix.RDNSequence, it is not possible to reliably marshal the +// parsed Issuer to it's original raw encoding. +func extractCRLIssuer(crlBytes []byte) ([]byte, error) { + if bytes.HasPrefix(crlBytes, crlPemPrefix) { + block, _ := pem.Decode(crlBytes) + if block != nil && block.Type == "X509 CRL" { + crlBytes = block.Bytes + } + } + + der := cryptobyte.String(crlBytes) + var issuer cryptobyte.String + if !der.ReadASN1(&der, cbasn1.SEQUENCE) || + !der.ReadASN1(&der, cbasn1.SEQUENCE) || + !der.SkipOptionalASN1(cbasn1.INTEGER) || + !der.SkipASN1(cbasn1.SEQUENCE) || + !der.ReadASN1Element(&issuer, cbasn1.SEQUENCE) { + return nil, errors.New("extractCRLIssuer: invalid ASN.1 encoding") + } + return issuer, nil +} diff --git a/security/advancedtls/crl_deprecated_test.go b/security/advancedtls/crl_deprecated_test.go new file mode 100644 index 000000000000..e16ec7ef2599 --- /dev/null +++ b/security/advancedtls/crl_deprecated_test.go @@ -0,0 +1,775 @@ +// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported +//go:build !go1.19 + +/* + * + * Copyright 2021 gRPC authors. + * + * 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 advancedtls + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "encoding/pem" + "fmt" + "math/big" + "net" + "os" + "path" + "strings" + "testing" + "time" + + lru "github.com/hashicorp/golang-lru" + "google.golang.org/grpc/security/advancedtls/testdata" +) + +func TestX509NameHash(t *testing.T) { + nameTests := []struct { + in pkix.Name + out string + }{ + { + in: pkix.Name{ + Country: []string{"US"}, + Organization: []string{"Example"}, + }, + out: "9cdd41ff", + }, + { + in: pkix.Name{ + Country: []string{"us"}, + Organization: []string{"example"}, + }, + out: "9cdd41ff", + }, + { + in: pkix.Name{ + Country: []string{" us"}, + Organization: []string{"example"}, + }, + out: "9cdd41ff", + }, + { + in: pkix.Name{ + Country: []string{"US"}, + Province: []string{"California"}, + Locality: []string{"Mountain View"}, + Organization: []string{"BoringSSL"}, + }, + out: "c24414d9", + }, + { + in: pkix.Name{ + Country: []string{"US"}, + Province: []string{"California"}, + Locality: []string{"Mountain View"}, + Organization: []string{"BoringSSL"}, + }, + out: "c24414d9", + }, + { + in: pkix.Name{ + SerialNumber: "87f4514475ba0a2b", + }, + out: "9dc713cd", + }, + { + in: pkix.Name{ + Country: []string{"US"}, + Province: []string{"California"}, + Locality: []string{"Mountain View"}, + Organization: []string{"Google LLC"}, + OrganizationalUnit: []string{"Production", "campus-sln"}, + CommonName: "Root CA (2021-02-02T07:30:36-08:00)", + }, + out: "0b35a562", + }, + { + in: pkix.Name{ + ExtraNames: []pkix.AttributeTypeAndValue{ + {Type: asn1.ObjectIdentifier{5, 5, 5, 5}, Value: "aaaa"}, + }, + }, + out: "eea339da", + }, + } + for _, tt := range nameTests { + t.Run(tt.in.String(), func(t *testing.T) { + h := x509NameHash(tt.in.ToRDNSequence()) + if h != tt.out { + t.Errorf("x509NameHash(%v): Got %v wanted %v", tt.in, h, tt.out) + } + }) + } +} + +func TestUnsupportedCRLs(t *testing.T) { + crlBytesSomeReasons := []byte(`-----BEGIN X509 CRL----- +MIIEeDCCA2ACAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVVMxHjAcBgNV +BAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczETMBEGA1UEAxMKR1RTIENBIDFPMRcN +MjEwNDI2MTI1OTQxWhcNMjEwNTA2MTE1OTQwWjCCAn0wIgIRAPOOG3L4VLC7CAAA +AABxQgEXDTIxMDQxOTEyMTgxOFowIQIQUK0UwBZkVdQIAAAAAHFCBRcNMjEwNDE5 +MTIxODE4WjAhAhBRIXBJaKoQkQgAAAAAcULHFw0yMTA0MjAxMjE4MTdaMCICEQCv +qQWUq5UxmQgAAAAAcULMFw0yMTA0MjAxMjE4MTdaMCICEQDdv5k1kKwKTQgAAAAA +cUOQFw0yMTA0MjExMjE4MTZaMCICEQDGIEfR8N9sEAgAAAAAcUOWFw0yMTA0MjEx +MjE4MThaMCECEBHgbLXlj5yUCAAAAABxQ/IXDTIxMDQyMTIzMDAyNlowIQIQE1wT +2GGYqKwIAAAAAHFD7xcNMjEwNDIxMjMwMDI5WjAiAhEAo/bSyDjpVtsIAAAAAHFE +txcNMjEwNDIyMjMwMDI3WjAhAhARdCrSrHE0dAgAAAAAcUS/Fw0yMTA0MjIyMzAw +MjhaMCECEHONohfWn3wwCAAAAABxRX8XDTIxMDQyMzIzMDAyOVowIgIRAOYkiUPA +os4vCAAAAABxRYgXDTIxMDQyMzIzMDAyOFowIQIQRNTow5Eg2gEIAAAAAHFGShcN +MjEwNDI0MjMwMDI2WjAhAhBX32dH4/WQ6AgAAAAAcUZNFw0yMTA0MjQyMzAwMjZa +MCICEQDHnUM1vsaP/wgAAAAAcUcQFw0yMTA0MjUyMzAwMjZaMCECEEm5rvmL8sj6 +CAAAAABxRxQXDTIxMDQyNTIzMDAyN1owIQIQW16OQs4YQYkIAAAAAHFIABcNMjEw +NDI2MTI1NDA4WjAhAhAhSohpYsJtDQgAAAAAcUgEFw0yMTA0MjYxMjU0MDlaoGkw +ZzAfBgNVHSMEGDAWgBSY0fhuEOvPm+xgnxiQG6DrfQn9KzALBgNVHRQEBAICBngw +NwYDVR0cAQH/BC0wK6AmoCSGImh0dHA6Ly9jcmwucGtpLmdvb2cvR1RTMU8xY29y +ZS5jcmyBAf8wDQYJKoZIhvcNAQELBQADggEBADPBXbxVxMJ1HC7btXExRUpJHUlU +YbeCZGx6zj5F8pkopbmpV7cpewwhm848Fx4VaFFppZQZd92O08daEC6aEqoug4qF +z6ZrOLzhuKfpW8E93JjgL91v0FYN7iOcT7+ERKCwVEwEkuxszxs7ggW6OJYJNvHh +priIdmcPoiQ3ZrIRH0vE3BfUcNXnKFGATWuDkiRI0I4A5P7NiOf+lAuGZet3/eom +0chgts6sdau10GfeUpHUd4f8e93cS/QeLeG16z7LC8vRLstU3m3vrknpZbdGqSia +97w66mqcnQh9V0swZiEnVLmLufaiuDZJ+6nUzSvLqBlb/ei3T/tKV0BoKJA= +-----END X509 CRL-----`) + + crlBytesIndirect := []byte(`-----BEGIN X509 CRL----- +MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU +ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg +Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 +MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG +EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 +MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 +MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV +BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j +BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ +BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG +SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS +TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG +NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq +XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF +6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 +qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 +-----END X509 CRL-----`) + + var tests = []struct { + desc string + in []byte + }{ + { + desc: "some reasons", + in: crlBytesSomeReasons, + }, + { + desc: "indirect", + in: crlBytesIndirect, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + crl, err := x509.ParseCRL(tt.in) + if err != nil { + t.Fatal(err) + } + if _, err := parseCRLExtensions(crl); err == nil { + t.Error("expected error got ok") + } + }) + } +} + +func TestCheckCertRevocation(t *testing.T) { + dummyCrlFile := []byte(`-----BEGIN X509 CRL----- +MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU +ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg +Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 +MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG +EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 +MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 +MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV +BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j +BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ +BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG +SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS +TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG +NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq +XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF +6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 +qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 +-----END X509 CRL-----`) + crl, err := x509.ParseCRL(dummyCrlFile) + if err != nil { + t.Fatalf("x509.ParseCRL(dummyCrlFile) failed: %v", err) + } + crlExt := &CRL{certList: crl} + var crlIssuer pkix.Name + crlIssuer.FillFromRDNSequence(&crl.TBSCertList.Issuer) + + var revocationTests = []struct { + desc string + in x509.Certificate + revoked RevocationStatus + }{ + { + desc: "Single revoked", + in: x509.Certificate{ + Issuer: pkix.Name{ + Country: []string{"USA"}, + Locality: []string{"here"}, + Organization: []string{"us"}, + CommonName: "Test1", + }, + SerialNumber: big.NewInt(2), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationRevoked, + }, + { + desc: "Revoked no entry issuer", + in: x509.Certificate{ + Issuer: pkix.Name{ + Country: []string{"USA"}, + Locality: []string{"here"}, + Organization: []string{"us"}, + CommonName: "Test1", + }, + SerialNumber: big.NewInt(3), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationRevoked, + }, + { + desc: "Revoked new entry issuer", + in: x509.Certificate{ + Issuer: pkix.Name{ + Country: []string{"USA"}, + Locality: []string{"here"}, + Organization: []string{"us"}, + CommonName: "Test2", + }, + SerialNumber: big.NewInt(4), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationRevoked, + }, + { + desc: "Single unrevoked", + in: x509.Certificate{ + Issuer: pkix.Name{ + Country: []string{"USA"}, + Locality: []string{"here"}, + Organization: []string{"us"}, + CommonName: "Test2", + }, + SerialNumber: big.NewInt(1), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationUnrevoked, + }, + { + desc: "Single unrevoked Issuer", + in: x509.Certificate{ + Issuer: crlIssuer, + SerialNumber: big.NewInt(2), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationUnrevoked, + }, + } + + for _, tt := range revocationTests { + rawIssuer, err := asn1.Marshal(tt.in.Issuer.ToRDNSequence()) + if err != nil { + t.Fatalf("asn1.Marshal(%v) failed: %v", tt.in.Issuer.ToRDNSequence(), err) + } + tt.in.RawIssuer = rawIssuer + t.Run(tt.desc, func(t *testing.T) { + rev, err := checkCertRevocation(&tt.in, crlExt) + if err != nil { + t.Errorf("checkCertRevocation(%v) err = %v", tt.in.Issuer, err) + } else if rev != tt.revoked { + t.Errorf("checkCertRevocation(%v(%v)) returned %v wanted %v", + tt.in.Issuer, tt.in.SerialNumber, rev, tt.revoked) + } + }) + } +} + +func makeChain(t *testing.T, name string) []*x509.Certificate { + t.Helper() + + certChain := make([]*x509.Certificate, 0) + + rest, err := os.ReadFile(name) + if err != nil { + t.Fatalf("os.ReadFile(%v) failed %v", name, err) + } + for len(rest) > 0 { + var block *pem.Block + block, rest = pem.Decode(rest) + c, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatalf("ParseCertificate error %v", err) + } + t.Logf("Parsed Cert sub = %v iss = %v", c.Subject, c.Issuer) + certChain = append(certChain, c) + } + return certChain +} + +func loadCRL(t *testing.T, path string) *CRL { + b, err := os.ReadFile(path) + if err != nil { + t.Fatalf("readFile(%v) failed err = %v", path, err) + } + crl, err := x509.ParseCRL(b) + if err != nil { + t.Fatalf("ParseCrl(%v) failed err = %v", path, err) + } + crlExt, err := parseCRLExtensions(crl) + if err != nil { + t.Fatalf("parseCRLExtensions(%v) failed err = %v", path, err) + } + crlExt.rawIssuer, err = extractCRLIssuer(b) + if err != nil { + t.Fatalf("extractCRLIssuer(%v) failed err= %v", path, err) + } + return crlExt +} + +func TestCachedCRL(t *testing.T) { + cache, err := lru.New(5) + if err != nil { + t.Fatalf("lru.New: err = %v", err) + } + + tests := []struct { + desc string + val interface{} + ok bool + }{ + { + desc: "Valid", + val: &CRL{ + certList: &pkix.CertificateList{ + TBSCertList: pkix.TBSCertificateList{ + NextUpdate: time.Now().Add(time.Hour), + }, + }}, + ok: true, + }, + { + desc: "Expired", + val: &CRL{ + certList: &pkix.CertificateList{ + TBSCertList: pkix.TBSCertificateList{ + NextUpdate: time.Now().Add(-time.Hour), + }, + }}, + ok: false, + }, + { + desc: "Wrong Type", + val: "string", + ok: false, + }, + { + desc: "Empty", + val: nil, + ok: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if tt.val != nil { + cache.Add(hex.EncodeToString([]byte(tt.desc)), tt.val) + } + _, ok := cachedCrl([]byte(tt.desc), cache) + if tt.ok != ok { + t.Errorf("Cache ok error expected %v vs %v", tt.ok, ok) + } + }) + } +} + +func TestGetIssuerCRLCache(t *testing.T) { + cache, err := lru.New(5) + if err != nil { + t.Fatalf("lru.New: err = %v", err) + } + + tests := []struct { + desc string + rawIssuer []byte + certs []*x509.Certificate + }{ + { + desc: "Valid", + rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + }, + { + desc: "Unverified", + rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, + }, + { + desc: "Not Found", + rawIssuer: []byte("not_found"), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + cache.Purge() + _, err := fetchIssuerCRL(tt.rawIssuer, tt.certs, RevocationConfig{ + RootDir: testdata.Path("."), + Cache: cache, + }) + if err == nil && cache.Len() == 0 { + t.Error("Verified CRL not added to cache") + } + if err != nil && cache.Len() != 0 { + t.Error("Unverified CRL added to cache") + } + }) + } +} + +func TestVerifyCrl(t *testing.T) { + tampered := loadCRL(t, testdata.Path("crl/1.crl")) + // Change the signature so it won't verify + tampered.certList.SignatureValue.Bytes[0]++ + + verifyTests := []struct { + desc string + crl *CRL + certs []*x509.Certificate + cert *x509.Certificate + errWant string + }{ + { + desc: "Pass intermediate", + crl: loadCRL(t, testdata.Path("crl/1.crl")), + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], + errWant: "", + }, + { + desc: "Pass leaf", + crl: loadCRL(t, testdata.Path("crl/2.crl")), + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[2], + errWant: "", + }, + { + desc: "Fail wrong cert chain", + crl: loadCRL(t, testdata.Path("crl/3.crl")), + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + cert: makeChain(t, testdata.Path("crl/revokedInt.pem"))[1], + errWant: "No certificates mached", + }, + { + desc: "Fail no certs", + crl: loadCRL(t, testdata.Path("crl/1.crl")), + certs: []*x509.Certificate{}, + cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], + errWant: "No certificates mached", + }, + { + desc: "Fail Tampered signature", + crl: tampered, + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], + errWant: "verification failure", + }, + } + + for _, tt := range verifyTests { + t.Run(tt.desc, func(t *testing.T) { + err := verifyCRL(tt.crl, tt.cert.RawIssuer, tt.certs) + switch { + case tt.errWant == "" && err != nil: + t.Errorf("Valid CRL did not verify err = %v", err) + case tt.errWant != "" && err == nil: + t.Error("Invalid CRL verified") + case tt.errWant != "" && !strings.Contains(err.Error(), tt.errWant): + t.Errorf("fetchIssuerCRL(_, %v, %v, _) = %v; want Contains(%v)", tt.cert.RawIssuer, tt.certs, err, tt.errWant) + } + }) + } +} + +func TestRevokedCert(t *testing.T) { + revokedIntChain := makeChain(t, testdata.Path("crl/revokedInt.pem")) + revokedLeafChain := makeChain(t, testdata.Path("crl/revokedLeaf.pem")) + validChain := makeChain(t, testdata.Path("crl/unrevoked.pem")) + cache, err := lru.New(5) + if err != nil { + t.Fatalf("lru.New: err = %v", err) + } + + var revocationTests = []struct { + desc string + in tls.ConnectionState + revoked bool + allowUndetermined bool + }{ + { + desc: "Single unrevoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain}}, + revoked: false, + }, + { + desc: "Single revoked intermediate", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedIntChain}}, + revoked: true, + }, + { + desc: "Single revoked leaf", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain}}, + revoked: true, + }, + { + desc: "Multi one revoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, revokedLeafChain}}, + revoked: false, + }, + { + desc: "Multi revoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain, revokedIntChain}}, + revoked: true, + }, + { + desc: "Multi unrevoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, validChain}}, + revoked: false, + }, + { + desc: "Undetermined revoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ + {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, + }}, + revoked: true, + }, + { + desc: "Undetermined allowed", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ + {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, + }}, + revoked: false, + allowUndetermined: true, + }, + } + + for _, tt := range revocationTests { + t.Run(tt.desc, func(t *testing.T) { + err := CheckRevocation(tt.in, RevocationConfig{ + RootDir: testdata.Path("crl"), + AllowUndetermined: tt.allowUndetermined, + Cache: cache, + }) + t.Logf("CheckRevocation err = %v", err) + if tt.revoked && err == nil { + t.Error("Revoked certificate chain was allowed") + } else if !tt.revoked && err != nil { + t.Error("Unrevoked certificate not allowed") + } + }) + } +} + +func setupTLSConn(t *testing.T) (net.Listener, *x509.Certificate, *ecdsa.PrivateKey) { + t.Helper() + templ := x509.Certificate{ + SerialNumber: big.NewInt(5), + BasicConstraintsValid: true, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour), + IsCA: true, + Subject: pkix.Name{CommonName: "test-cert"}, + KeyUsage: x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + IPAddresses: []net.IP{net.ParseIP("::1")}, + CRLDistributionPoints: []string{"http://static.corp.google.com/crl/campus-sln/borg"}, + } + + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("ecdsa.GenerateKey failed err = %v", err) + } + rawCert, err := x509.CreateCertificate(rand.Reader, &templ, &templ, key.Public(), key) + if err != nil { + t.Fatalf("x509.CreateCertificate failed err = %v", err) + } + cert, err := x509.ParseCertificate(rawCert) + if err != nil { + t.Fatalf("x509.ParseCertificate failed err = %v", err) + } + + srvCfg := tls.Config{ + Certificates: []tls.Certificate{ + { + Certificate: [][]byte{cert.Raw}, + PrivateKey: key, + }, + }, + } + l, err := tls.Listen("tcp6", "[::1]:0", &srvCfg) + if err != nil { + t.Fatalf("tls.Listen failed err = %v", err) + } + return l, cert, key +} + +// TestVerifyConnection will setup a client/server connection and check revocation in the real TLS dialer +func TestVerifyConnection(t *testing.T) { + lis, cert, key := setupTLSConn(t) + defer func() { + lis.Close() + }() + + var handshakeTests = []struct { + desc string + revoked []pkix.RevokedCertificate + success bool + }{ + { + desc: "Empty CRL", + revoked: []pkix.RevokedCertificate{}, + success: true, + }, + { + desc: "Revoked Cert", + revoked: []pkix.RevokedCertificate{ + { + SerialNumber: cert.SerialNumber, + RevocationTime: time.Now(), + }, + }, + success: false, + }, + } + for _, tt := range handshakeTests { + t.Run(tt.desc, func(t *testing.T) { + // Accept one connection. + go func() { + conn, err := lis.Accept() + if err != nil { + t.Errorf("tls.Accept failed err = %v", err) + } else { + conn.Write([]byte("Hello, World!")) + conn.Close() + } + }() + + dir, err := os.MkdirTemp("", "crl_dir") + if err != nil { + t.Fatalf("os.MkdirTemp failed err = %v", err) + } + defer os.RemoveAll(dir) + + crl, err := cert.CreateCRL(rand.Reader, key, tt.revoked, time.Now(), time.Now().Add(time.Hour)) + if err != nil { + t.Fatalf("templ.CreateCRL failed err = %v", err) + } + + err = os.WriteFile(path.Join(dir, fmt.Sprintf("%s.r0", x509NameHash(cert.Subject.ToRDNSequence()))), crl, 0777) + if err != nil { + t.Fatalf("os.WriteFile failed err = %v", err) + } + + cp := x509.NewCertPool() + cp.AddCert(cert) + cliCfg := tls.Config{ + RootCAs: cp, + VerifyConnection: func(cs tls.ConnectionState) error { + return CheckRevocation(cs, RevocationConfig{RootDir: dir}) + }, + } + conn, err := tls.Dial(lis.Addr().Network(), lis.Addr().String(), &cliCfg) + t.Logf("tls.Dial err = %v", err) + if tt.success && err != nil { + t.Errorf("Expected success got err = %v", err) + } + if !tt.success && err == nil { + t.Error("Expected error, but got success") + } + if err == nil { + conn.Close() + } + }) + } +} + +func TestIssuerNonPrintableString(t *testing.T) { + rawIssuer, err := hex.DecodeString("300c310a300806022a030c023a29") + if err != nil { + t.Fatalf("failed to decode issuer: %s", err) + } + _, err = fetchCRL(rawIssuer, RevocationConfig{RootDir: testdata.Path("crl")}) + if err != nil { + t.Fatalf("fetchCRL failed: %s", err) + } +} + +// TestCRLCacheExpirationReloading tests the basic expiration and reloading of a +// cached CRL. The setup places an empty CRL in the cache, and a corresponding +// CRL with a revocation in the CRL directory. We then validate the certificate +// to verify that the certificate is not revoked. Then, we modify the +// NextUpdate time to be in the past so that when we next check for revocation, +// the existing cache entry should be seen as expired, and the CRL in the +// directory showing `revokedInt.pem` as revoked will be loaded, resulting in +// the check returning `RevocationRevoked`. +func TestCRLCacheExpirationReloading(t *testing.T) { + cache, err := lru.New(5) + if err != nil { + t.Fatalf("Creating cache failed") + } + + var certs = makeChain(t, testdata.Path("crl/revokedInt.pem")) + // Certs[1] has the same issuer as the revoked cert + rawIssuer := certs[1].RawIssuer + + // `3.crl`` revokes `revokedInt.pem` + crl := loadCRL(t, testdata.Path("crl/3.crl")) + // Modify the crl so that the cert is NOT revoked and add it to the cache + crl.certList.TBSCertList.RevokedCertificates = nil + crl.certList.TBSCertList.NextUpdate = time.Now().Add(time.Hour) + cache.Add(hex.EncodeToString(rawIssuer), crl) + var cfg = RevocationConfig{RootDir: testdata.Path("crl"), Cache: cache} + revocationStatus := checkChain(certs, cfg) + if revocationStatus != RevocationUnrevoked { + t.Fatalf("Certificate check should be RevocationUnrevoked, was %v", revocationStatus) + } + + // Modify the entry in the cache so that the cache will be refreshed + crl.certList.TBSCertList.NextUpdate = time.Now() + cache.Add(hex.EncodeToString(rawIssuer), crl) + + revocationStatus = checkChain(certs, cfg) + if revocationStatus != RevocationRevoked { + t.Fatalf("A certificate should have been `RevocationRevoked` but was %v", revocationStatus) + } +} From 889895973c37d98dcc7448fc251c668c75d6e1cb Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Thu, 12 Oct 2023 01:23:19 +0000 Subject: [PATCH 37/62] Fit to grpctest.Tester pattern --- security/advancedtls/crl_provider_test.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 4f072a146d6b..379d764f6562 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -37,7 +37,7 @@ const nonCRLFilesUnderCRLDirectory = 5 // cases for CRL checks. It loads the CRLs under crl directory, constructs // unrevoked, revoked leaf, and revoked intermediate chains, as well as a chain // without CRL for issuer, and checks that it’s correctly processed. -func TestStaticCRLProvider(t *testing.T) { +func (s) TestStaticCRLProvider(t *testing.T) { rawCRLs := make([][]byte, 6) for i := 1; i <= 6; i++ { rawCRL, err := os.ReadFile(testdata.Path(fmt.Sprintf("crl/%d.crl", i))) @@ -91,7 +91,7 @@ func TestStaticCRLProvider(t *testing.T) { // TestFileWatcherCRLProviderConfig checks creation of FileWatcherCRLProvider, // and the validation of Options configuration. The configurations include empty // one, non existing CRLDirectory, invalid RefreshDuration, and the correct one. -func TestFileWatcherCRLProviderConfig(t *testing.T) { +func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { if _, err := MakeFileWatcherCRLProvider(Options{}); err == nil { t.Fatalf("Empty Options should not be allowed") } @@ -127,7 +127,7 @@ func TestFileWatcherCRLProviderConfig(t *testing.T) { // intermediate chains, as well as a chain without CRL for issuer, and check // that it’s correctly processed. Additionally, we also check if number of // invocations of custom callback is correct. -func TestFileWatcherCRLProvider(t *testing.T) { +func (s) TestFileWatcherCRLProvider(t *testing.T) { // testdata.Path("crl") contains 5 non-crl files. nonCRLFilesSet := make(map[string]struct{}) customCallback := func(err error) { @@ -194,7 +194,7 @@ func TestFileWatcherCRLProvider(t *testing.T) { // check if in-memory storage was properly updated. Please note that the same // instance of FileWatcherCRLProvider is used for the whole test so test cases // cases are not independent from each other. -func TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { +func (s) TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { sourcePath := testdata.Path("crl") targetPath := testdata.Path("crl/provider/filewatcher") p, err := MakeFileWatcherCRLProvider(Options{ @@ -259,6 +259,7 @@ func TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { } func copyFiles(sourcePath string, targetPath string, fileNames []string, t *testing.T) { + t.Helper() targetDir, err := os.Open(targetPath) if err != nil { t.Fatalf("Can't open dir %v: %v", targetPath, err) From b16af8b65412c0aaa98287fe269012b4bc9081e1 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 16 Oct 2023 15:16:53 +0000 Subject: [PATCH 38/62] Update readme for crl provider tests --- .../testdata/crl/provider/README.md | 75 +++++++++++++++++-- 1 file changed, 67 insertions(+), 8 deletions(-) diff --git a/security/advancedtls/testdata/crl/provider/README.md b/security/advancedtls/testdata/crl/provider/README.md index 0e5b9b6d7e07..26d53aec0ee0 100644 --- a/security/advancedtls/testdata/crl/provider/README.md +++ b/security/advancedtls/testdata/crl/provider/README.md @@ -1,14 +1,73 @@ -openssl req -x509 -newkey rsa:4096 -keyout server_trust_key.pem -out server_trust_cert.pem -days 365 -subj "/C=US/ST=VA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.ca.com" -nodes -openssl req -x509 -newkey rsa:4096 -keyout client_trust_key.pem -out client_trust_cert.pem -days 365 -subj "/C=US/ST=CA/L=SVL/O=Internet Widgits Pty Ltd" -nodes +About This Directory +------------- +The directory test data (certificates and CRLs) used for testing CRL providers +functionality. -openssl req -newkey rsa:4096 -keyout server_cert.key -out new_cert.csr -nodes -subj "/C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com" -sha256 -openssl x509 -req -in new_cert.csr -out server_cert.pem -CA client_trust_cert.pem -CAkey client_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile extensions.conf +How to Generate Test Data Using OpenSSL +------------- -openssl req -newkey rsa:4096 -keyout client_cert.key -out new_cert.csr -nodes -subj "/C=US/ST=CA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.com" -sha256 -openssl x509 -req -in new_cert.csr -out client_cert.pem -CA server_trust_cert.pem -CAkey server_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile extensions.conf +We need to generate the following artifacts for testing CRL provider: +* server self signed CA cert +* client self signed CA cert +* server cert signed by client CA +* client cert signed by server CA +* empty crl file +* crl file containing information about revoked server cert -#extensions.conf +Please find the related commands below. + +* Generate self signed CAs +``` +$ openssl req -x509 -newkey rsa:4096 -keyout server_trust_key.pem -out server_trust_cert.pem -days 365 -subj "/C=US/ST=VA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.ca.com" -nodes +$ openssl req -x509 -newkey rsa:4096 -keyout client_trust_key.pem -out client_trust_cert.pem -days 365 -subj "/C=US/ST=CA/L=SVL/O=Internet Widgits Pty Ltd" -nodes +``` + +* Generate client and server certs signed by CAs +``` +$ openssl req -newkey rsa:4096 -keyout server_cert.key -out new_cert.csr -nodes -subj "/C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com" -sha256 +$ openssl x509 -req -in new_cert.csr -out server_cert.pem -CA client_trust_cert.pem -CAkey client_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile extensions.conf + +$ openssl req -newkey rsa:4096 -keyout client_cert.key -out new_cert.csr -nodes -subj "/C=US/ST=CA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.com" -sha256 +$ openssl x509 -req -in new_cert.csr -out client_cert.pem -CA server_trust_cert.pem -CAkey server_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile extensions.conf +``` + +Here is the content of `extensions.conf` - +``` subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer basicConstraints = CA:FALSE -keyUsage = digitalSignature, keyEncipherment \ No newline at end of file +keyUsage = digitalSignature, keyEncipherment +``` + +* Generate CRLs +For CRL generation we need 2 more files called `index.txt` and `crlnumber.txt`: +``` +$ echo "1000" > crlnumber.txt +$ touch index.txt +``` +Also we need anothe config `crl.cnf` - +``` +[ ca ] +default_ca = my_ca + +[ my_ca ] +crl = crl.pem +default_md = sha256 +database = index.txt +crlnumber = crlnumber.txt +default_crl_days = 30 +default_crl_hours = 1 +crl_extensions = crl_ext + +[crl_ext] +# Authority Key Identifier extension +authorityKeyIdentifier=keyid:always,issuer:always +``` + +The commands to generate empty CRL file and CRL file containing revoked server +cert are below. +``` +$ openssl ca -gencrl -keyfile client_trust_key.pem -cert client_trust_cert.pem -out crl_empty.pem -config crl.cnf +$ openssl ca -revoke server_cert.pem -keyfile client_trust_key.pem -cert client_trust_cert.pem -config crl.cnf +$ openssl ca -gencrl -keyfile client_trust_key.pem -cert client_trust_cert.pem -out crl_server_revoked.pem -config crl.cnf +``` \ No newline at end of file From 21f430135c17da5117b16496103ea74b97a72c4b Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 16 Oct 2023 16:22:51 +0000 Subject: [PATCH 39/62] Address PR comments --- security/advancedtls/crl.go | 4 ++-- security/advancedtls/crl_provider.go | 14 +++++++------- security/advancedtls/crl_provider_test.go | 1 - 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index 89f403d90b51..a446b5ab5f46 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -116,8 +116,8 @@ func NewCRL(b []byte) (*CRL, error) { return crlExt, nil } -// ReadCRLFile reads a file from the provided path, and returns constructed -// from it. +// ReadCRLFile reads a file from the provided path, and returns constructed CRL +// struct from it. func ReadCRLFile(path string) (*CRL, error) { b, err := os.ReadFile(path) if err != nil { diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index df595b83f770..44ca619ce089 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -31,10 +31,13 @@ const defaultCRLRefreshDuration = 1 * time.Hour // CRLProvider is the interface to be implemented to enable custom CRL provider // behavior. // -// The interface defines how the data is read, but doesn't prescribe a way -// CRL are loaded and stored. Such implementations can be used in -// RevocationConfig of advancedtls.ClientOptions and/or -// advancedtls.ServerOptions . +// The interface defines how gRPC gets CRLs from the provider during handshakes, +// but doesn't prescribe a specific way to load and store CRLs. Such +// implementations can be used in RevocationConfig of advancedtls.ClientOptions +// and/or advancedtls.ServerOptions. +// Please note that checking CRLs is being directly on the path of connection +// establishment, so implementations of the CRL function need to be fast, and +// slow things such as file IO should be done asynchronously. // TODO(erm-g): Add link to related gRFC once it's ready. // Please refer to https://github.com/grpc/proposal/ for more details. type CRLProvider interface { @@ -47,9 +50,6 @@ type CRLProvider interface { // StaticCRLProvider implements CRLProvider interface by accepting raw content // of CRL files at creation time and storing parsed CRL structs in-memory. type StaticCRLProvider struct { - // TODO CRL is sort of our internal representation - provide an API for - // people to read into it, or provide a simpler type in the API then - // internally convert to this form crls map[string]*CRL } diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 379d764f6562..1a3c93fad722 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -128,7 +128,6 @@ func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { // that it’s correctly processed. Additionally, we also check if number of // invocations of custom callback is correct. func (s) TestFileWatcherCRLProvider(t *testing.T) { - // testdata.Path("crl") contains 5 non-crl files. nonCRLFilesSet := make(map[string]struct{}) customCallback := func(err error) { nonCRLFilesSet[err.Error()] = struct{}{} From 51b42aa9298fef38e238dfd8f2e090fdf80c4eeb Mon Sep 17 00:00:00 2001 From: Gregory Cooke Date: Mon, 16 Oct 2023 18:50:18 +0000 Subject: [PATCH 40/62] Revert "Restoring deprecated crl files" This reverts commit 56437603a432b36deee8657bb947f49f10b8767b. --- security/advancedtls/crl_deprecated.go | 521 ------------- security/advancedtls/crl_deprecated_test.go | 775 -------------------- 2 files changed, 1296 deletions(-) delete mode 100644 security/advancedtls/crl_deprecated.go delete mode 100644 security/advancedtls/crl_deprecated_test.go diff --git a/security/advancedtls/crl_deprecated.go b/security/advancedtls/crl_deprecated.go deleted file mode 100644 index cd7b3d1228ad..000000000000 --- a/security/advancedtls/crl_deprecated.go +++ /dev/null @@ -1,521 +0,0 @@ -// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported -//go:build !go1.19 - -/* - * - * Copyright 2021 gRPC authors. - * - * 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 advancedtls - -import ( - "bytes" - "crypto/sha1" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/binary" - "encoding/hex" - "encoding/pem" - "errors" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - "golang.org/x/crypto/cryptobyte" - cbasn1 "golang.org/x/crypto/cryptobyte/asn1" - "google.golang.org/grpc/grpclog" -) - -var grpclogLogger = grpclog.Component("advancedtls") - -// Cache is an interface to cache CRL files. -// The cache implementation must be concurrency safe. -// A fixed size lru cache from golang-lru is recommended. -type Cache interface { - // Add adds a value to the cache. - Add(key, value interface{}) bool - // Get looks up a key's value from the cache. - Get(key interface{}) (value interface{}, ok bool) -} - -// RevocationConfig contains options for CRL lookup. -type RevocationConfig struct { - // RootDir is the directory to search for CRL files. - // Directory format must match OpenSSL X509_LOOKUP_hash_dir(3). - RootDir string - // AllowUndetermined controls if certificate chains with RevocationUndetermined - // revocation status are allowed to complete. - AllowUndetermined bool - // Cache will store CRL files if not nil, otherwise files are reloaded for every lookup. - Cache Cache -} - -// RevocationStatus is the revocation status for a certificate or chain. -type RevocationStatus int - -const ( - // RevocationUndetermined means we couldn't find or verify a CRL for the cert. - RevocationUndetermined RevocationStatus = iota - // RevocationUnrevoked means we found the CRL for the cert and the cert is not revoked. - RevocationUnrevoked - // RevocationRevoked means we found the CRL and the cert is revoked. - RevocationRevoked -) - -func (s RevocationStatus) String() string { - return [...]string{"RevocationUndetermined", "RevocationUnrevoked", "RevocationRevoked"}[s] -} - -// CRL contains a pkix.CertificateList and parsed -// extensions that aren't provided by the golang CRL parser. -type CRL struct { - CertList *pkix.CertificateList - // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. - AuthorityKeyID []byte - RawIssuer []byte -} - -const tagDirectoryName = 4 - -var ( - // RFC5280, 5.2.4 id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } - oidDeltaCRLIndicator = asn1.ObjectIdentifier{2, 5, 29, 27} - // RFC5280, 5.2.5 id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } - oidIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28} - // RFC5280, 5.3.3 id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } - oidCertificateIssuer = asn1.ObjectIdentifier{2, 5, 29, 29} - // RFC5290, 4.2.1.1 id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } - oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35} -) - -// x509NameHash implements the OpenSSL X509_NAME_hash function for hashed directory lookups. -// -// NOTE: due to the behavior of asn1.Marshal, if the original encoding of the RDN sequence -// contains strings which do not use the ASN.1 PrintableString type, the name will not be -// re-encoded using those types, resulting in a hash which does not match that produced -// by OpenSSL. -func x509NameHash(r pkix.RDNSequence) string { - var canonBytes []byte - // First, canonicalize all the strings. - for _, rdnSet := range r { - for i, rdn := range rdnSet { - value, ok := rdn.Value.(string) - if !ok { - continue - } - // OpenSSL trims all whitespace, does a tolower, and removes extra spaces between words. - // Implemented in x509_name_canon in OpenSSL - canonStr := strings.Join(strings.Fields( - strings.TrimSpace(strings.ToLower(value))), " ") - // Then it changes everything to UTF8 strings - rdnSet[i].Value = asn1.RawValue{Tag: asn1.TagUTF8String, Bytes: []byte(canonStr)} - - } - } - - // Finally, OpenSSL drops the initial sequence tag - // so we marshal all the RDNs separately instead of as a group. - for _, canonRdn := range r { - b, err := asn1.Marshal(canonRdn) - if err != nil { - continue - } - canonBytes = append(canonBytes, b...) - } - - issuerHash := sha1.Sum(canonBytes) - // Openssl takes the first 4 bytes and encodes them as a little endian - // uint32 and then uses the hex to make the file name. - // In C++, this would be: - // (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | - // ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) - // ) & 0xffffffffL; - fileHash := binary.LittleEndian.Uint32(issuerHash[0:4]) - return fmt.Sprintf("%08x", fileHash) -} - -// CheckRevocation checks the connection for revoked certificates based on RFC5280. -// This implementation has the following major limitations: -// - Indirect CRL files are not supported. -// - CRL loading is only supported from directories in the X509_LOOKUP_hash_dir format. -// - OnlySomeReasons is not supported. -// - Delta CRL files are not supported. -// - Certificate CRLDistributionPoint must be URLs, but are then ignored and converted into a file path. -// - CRL checks are done after path building, which goes against RFC4158. -func CheckRevocation(conn tls.ConnectionState, cfg RevocationConfig) error { - return CheckChainRevocation(conn.VerifiedChains, cfg) -} - -// CheckChainRevocation checks the verified certificate chain -// for revoked certificates based on RFC5280. -func CheckChainRevocation(verifiedChains [][]*x509.Certificate, cfg RevocationConfig) error { - // Iterate the verified chains looking for one that is RevocationUnrevoked. - // A single RevocationUnrevoked chain is enough to allow the connection, and a single RevocationRevoked - // chain does not mean the connection should fail. - count := make(map[RevocationStatus]int) - for _, chain := range verifiedChains { - switch checkChain(chain, cfg) { - case RevocationUnrevoked: - // If any chain is RevocationUnrevoked then return no error. - return nil - case RevocationRevoked: - // If this chain is revoked, keep looking for another chain. - count[RevocationRevoked]++ - continue - case RevocationUndetermined: - if cfg.AllowUndetermined { - return nil - } - count[RevocationUndetermined]++ - continue - } - } - return fmt.Errorf("no unrevoked chains found: %v", count) -} - -// checkChain will determine and check all certificates in chain against the CRL -// defined in the certificate with the following rules: -// 1. If any certificate is RevocationRevoked, return RevocationRevoked. -// 2. If any certificate is RevocationUndetermined, return RevocationUndetermined. -// 3. If all certificates are RevocationUnrevoked, return RevocationUnrevoked. -func checkChain(chain []*x509.Certificate, cfg RevocationConfig) RevocationStatus { - chainStatus := RevocationUnrevoked - for _, c := range chain { - switch checkCert(c, chain, cfg) { - case RevocationRevoked: - // Easy case, if a cert in the chain is revoked, the chain is revoked. - return RevocationRevoked - case RevocationUndetermined: - // If we couldn't find the revocation status for a cert, the chain is at best RevocationUndetermined - // keep looking to see if we find a cert in the chain that's RevocationRevoked, - // but return RevocationUndetermined at a minimum. - chainStatus = RevocationUndetermined - case RevocationUnrevoked: - // Continue iterating up the cert chain. - continue - } - } - return chainStatus -} - -func cachedCrl(rawIssuer []byte, cache Cache) (*CRL, bool) { - val, ok := cache.Get(hex.EncodeToString(rawIssuer)) - if !ok { - return nil, false - } - crl, ok := val.(*CRL) - if !ok { - return nil, false - } - // If the CRL is expired, force a reload. - if crl.CertList.HasExpired(time.Now()) { - return nil, false - } - return crl, true -} - -// fetchIssuerCRL fetches and verifies the CRL for rawIssuer from disk or cache if configured in cfg. -func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*CRL, error) { - if cfg.Cache != nil { - if crl, ok := cachedCrl(rawIssuer, cfg.Cache); ok { - return crl, nil - } - } - - crl, err := fetchCRL(rawIssuer, cfg) - if err != nil { - return nil, fmt.Errorf("fetchCRL() failed: %v", err) - } - - if err := verifyCRL(crl, rawIssuer, crlVerifyCrt); err != nil { - return nil, fmt.Errorf("verifyCRL() failed: %v", err) - } - if cfg.Cache != nil { - cfg.Cache.Add(hex.EncodeToString(rawIssuer), crl) - } - return crl, nil -} - -// checkCert checks a single certificate against the CRL defined in the certificate. -// It will fetch and verify the CRL(s) defined in the root directory specified by cfg. -// If we can't load any authoritative CRL files, the status is RevocationUndetermined. -// c is the certificate to check. -// crlVerifyCrt is the group of possible certificates to verify the crl. -func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) RevocationStatus { - crl, err := fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) - if err != nil { - // We couldn't load any CRL files for the certificate, so we don't know if it's RevocationUnrevoked or not. - grpclogLogger.Warningf("getIssuerCRL(%v) err = %v", c.Issuer, err) - return RevocationUndetermined - } - revocation, err := checkCertRevocation(c, crl) - if err != nil { - grpclogLogger.Warningf("checkCertRevocation(CRL %v) failed: %v", crl.CertList.TBSCertList.Issuer, err) - // We couldn't check the CRL file for some reason, so we don't know if it's RevocationUnrevoked or not. - return RevocationUndetermined - } - // Here we've gotten a CRL that loads and verifies. - // We only handle all-reasons CRL files, so this file - // is authoritative for the certificate. - return revocation -} - -func checkCertRevocation(c *x509.Certificate, crl *CRL) (RevocationStatus, error) { - // Per section 5.3.3 we prime the certificate issuer with the CRL issuer. - // Subsequent entries use the previous entry's issuer. - rawEntryIssuer := crl.RawIssuer - - // Loop through all the revoked certificates. - for _, revCert := range crl.CertList.TBSCertList.RevokedCertificates { - // 5.3 Loop through CRL entry extensions for needed information. - for _, ext := range revCert.Extensions { - if oidCertificateIssuer.Equal(ext.Id) { - extIssuer, err := parseCertIssuerExt(ext) - if err != nil { - grpclogLogger.Info(err) - if ext.Critical { - return RevocationUndetermined, err - } - // Since this is a non-critical extension, we can skip it even though - // there was a parsing failure. - continue - } - rawEntryIssuer = extIssuer - } else if ext.Critical { - return RevocationUndetermined, fmt.Errorf("checkCertRevocation: Unhandled critical extension: %v", ext.Id) - } - } - - // If the issuer and serial number appear in the CRL, the certificate is revoked. - if bytes.Equal(c.RawIssuer, rawEntryIssuer) && c.SerialNumber.Cmp(revCert.SerialNumber) == 0 { - // CRL contains the serial, so return revoked. - return RevocationRevoked, nil - } - } - // We did not find the serial in the CRL file that was valid for the cert - // so the certificate is not revoked. - return RevocationUnrevoked, nil -} - -func parseCertIssuerExt(ext pkix.Extension) ([]byte, error) { - // 5.3.3 Certificate Issuer - // CertificateIssuer ::= GeneralNames - // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - var generalNames []asn1.RawValue - if rest, err := asn1.Unmarshal(ext.Value, &generalNames); err != nil || len(rest) != 0 { - return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) - } - - for _, generalName := range generalNames { - // GeneralName ::= CHOICE { - // otherName [0] OtherName, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER } - if generalName.Tag == tagDirectoryName { - return generalName.Bytes, nil - } - } - // Conforming CRL issuers MUST include in this extension the - // distinguished name (DN) from the issuer field of the certificate that - // corresponds to this CRL entry. - // If we couldn't get a directoryName, we can't reason about this file so cert status is - // RevocationUndetermined. - return nil, errors.New("no DN found in certificate issuer") -} - -// RFC 5280, 4.2.1.1 -type authKeyID struct { - ID []byte `asn1:"optional,tag:0"` -} - -// RFC5280, 5.2.5 -// id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } - -// IssuingDistributionPoint ::= SEQUENCE { -// distributionPoint [0] DistributionPointName OPTIONAL, -// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, -// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, -// onlySomeReasons [3] ReasonFlags OPTIONAL, -// indirectCRL [4] BOOLEAN DEFAULT FALSE, -// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } - -// -- at most one of onlyContainsUserCerts, onlyContainsCACerts, -// -- and onlyContainsAttributeCerts may be set to TRUE. -type issuingDistributionPoint struct { - DistributionPoint asn1.RawValue `asn1:"optional,tag:0"` - OnlyContainsUserCerts bool `asn1:"optional,tag:1"` - OnlyContainsCACerts bool `asn1:"optional,tag:2"` - OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"` - IndirectCRL bool `asn1:"optional,tag:4"` - OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"` -} - -// parseCRLExtensions parses the extensions for a CRL -// and checks that they're supported by the parser. -func parseCRLExtensions(c *pkix.CertificateList) (*CRL, error) { - if c == nil { - return nil, errors.New("c is nil, expected any value") - } - certList := &CRL{CertList: c} - - for _, ext := range c.TBSCertList.Extensions { - switch { - case oidDeltaCRLIndicator.Equal(ext.Id): - return nil, fmt.Errorf("delta CRLs unsupported") - - case oidAuthorityKeyIdentifier.Equal(ext.Id): - var a authKeyID - if rest, err := asn1.Unmarshal(ext.Value, &a); err != nil { - return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) - } else if len(rest) != 0 { - return nil, errors.New("trailing data after AKID extension") - } - certList.AuthorityKeyID = a.ID - - case oidIssuingDistributionPoint.Equal(ext.Id): - var dp issuingDistributionPoint - if rest, err := asn1.Unmarshal(ext.Value, &dp); err != nil { - return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) - } else if len(rest) != 0 { - return nil, errors.New("trailing data after IssuingDistributionPoint extension") - } - - if dp.OnlyContainsUserCerts || dp.OnlyContainsCACerts || dp.OnlyContainsAttributeCerts { - return nil, errors.New("CRL only contains some certificate types") - } - if dp.IndirectCRL { - return nil, errors.New("indirect CRLs unsupported") - } - if dp.OnlySomeReasons.BitLength != 0 { - return nil, errors.New("onlySomeReasons unsupported") - } - - case ext.Critical: - return nil, fmt.Errorf("unsupported critical extension: %v", ext.Id) - } - } - - if len(certList.AuthorityKeyID) == 0 { - return nil, errors.New("authority key identifier extension missing") - } - return certList, nil -} - -func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { - var parsedCRL *CRL - // 6.3.3 (a) (1) (ii) - // According to X509_LOOKUP_hash_dir the format is issuer_hash.rN where N is an increasing number. - // There are no gaps, so we break when we can't find a file. - for i := 0; ; i++ { - // Unmarshal to RDNSeqence according to http://go/godoc/crypto/x509/pkix/#Name. - var r pkix.RDNSequence - rest, err := asn1.Unmarshal(rawIssuer, &r) - if len(rest) != 0 || err != nil { - return nil, fmt.Errorf("asn1.Unmarshal(Issuer) len(rest) = %d failed: %v", len(rest), err) - } - crlPath := fmt.Sprintf("%s.r%d", filepath.Join(cfg.RootDir, x509NameHash(r)), i) - crlBytes, err := os.ReadFile(crlPath) - if err != nil { - // Break when we can't read a CRL file. - grpclogLogger.Infof("readFile: %v", err) - break - } - - crl, err := x509.ParseCRL(crlBytes) - if err != nil { - // Parsing errors for a CRL shouldn't happen so fail. - return nil, fmt.Errorf("x509.ParseCrl(%v) failed: %v", crlPath, err) - } - var certList *CRL - if certList, err = parseCRLExtensions(crl); err != nil { - grpclogLogger.Infof("fetchCRL: unsupported crl %v: %v", crlPath, err) - // Continue to find a supported CRL - continue - } - - rawCRLIssuer, err := extractCRLIssuer(crlBytes) - if err != nil { - return nil, err - } - certList.RawIssuer = rawCRLIssuer - // RFC5280, 6.3.3 (b) Verify the issuer and scope of the complete CRL. - if bytes.Equal(rawIssuer, rawCRLIssuer) { - parsedCRL = certList - // Continue to find the highest number in the .rN suffix. - continue - } - } - - if parsedCRL == nil { - return nil, fmt.Errorf("fetchCrls no CRLs found for issuer") - } - return parsedCRL, nil -} - -func verifyCRL(crl *CRL, rawIssuer []byte, chain []*x509.Certificate) error { - // RFC5280, 6.3.3 (f) Obtain and validateate the certification path for the issuer of the complete CRL - // We intentionally limit our CRLs to be signed with the same certificate path as the certificate - // so we can use the chain from the connection. - - for _, c := range chain { - // Use the key where the subject and KIDs match. - // This departs from RFC4158, 3.5.12 which states that KIDs - // cannot eliminate certificates, but RFC5280, 5.2.1 states that - // "Conforming CRL issuers MUST use the key identifier method, and MUST - // include this extension in all CRLs issued." - // So, this is much simpler than RFC4158 and should be compatible. - if bytes.Equal(c.SubjectKeyId, crl.AuthorityKeyID) && bytes.Equal(c.RawSubject, crl.RawIssuer) { - // RFC5280, 6.3.3 (g) Validate signature. - return c.CheckCRLSignature(crl.CertList) - } - } - return fmt.Errorf("verifyCRL: No certificates mached CRL issuer (%v)", crl.CertList.TBSCertList.Issuer) -} - -var crlPemPrefix = []byte("-----BEGIN X509 CRL") - -// extractCRLIssuer extracts the raw ASN.1 encoding of the CRL issuer. Due to the design of -// pkix.CertificateList and pkix.RDNSequence, it is not possible to reliably marshal the -// parsed Issuer to it's original raw encoding. -func extractCRLIssuer(crlBytes []byte) ([]byte, error) { - if bytes.HasPrefix(crlBytes, crlPemPrefix) { - block, _ := pem.Decode(crlBytes) - if block != nil && block.Type == "X509 CRL" { - crlBytes = block.Bytes - } - } - - der := cryptobyte.String(crlBytes) - var issuer cryptobyte.String - if !der.ReadASN1(&der, cbasn1.SEQUENCE) || - !der.ReadASN1(&der, cbasn1.SEQUENCE) || - !der.SkipOptionalASN1(cbasn1.INTEGER) || - !der.SkipASN1(cbasn1.SEQUENCE) || - !der.ReadASN1Element(&issuer, cbasn1.SEQUENCE) { - return nil, errors.New("extractCRLIssuer: invalid ASN.1 encoding") - } - return issuer, nil -} diff --git a/security/advancedtls/crl_deprecated_test.go b/security/advancedtls/crl_deprecated_test.go deleted file mode 100644 index e16ec7ef2599..000000000000 --- a/security/advancedtls/crl_deprecated_test.go +++ /dev/null @@ -1,775 +0,0 @@ -// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported -//go:build !go1.19 - -/* - * - * Copyright 2021 gRPC authors. - * - * 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 advancedtls - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/hex" - "encoding/pem" - "fmt" - "math/big" - "net" - "os" - "path" - "strings" - "testing" - "time" - - lru "github.com/hashicorp/golang-lru" - "google.golang.org/grpc/security/advancedtls/testdata" -) - -func TestX509NameHash(t *testing.T) { - nameTests := []struct { - in pkix.Name - out string - }{ - { - in: pkix.Name{ - Country: []string{"US"}, - Organization: []string{"Example"}, - }, - out: "9cdd41ff", - }, - { - in: pkix.Name{ - Country: []string{"us"}, - Organization: []string{"example"}, - }, - out: "9cdd41ff", - }, - { - in: pkix.Name{ - Country: []string{" us"}, - Organization: []string{"example"}, - }, - out: "9cdd41ff", - }, - { - in: pkix.Name{ - Country: []string{"US"}, - Province: []string{"California"}, - Locality: []string{"Mountain View"}, - Organization: []string{"BoringSSL"}, - }, - out: "c24414d9", - }, - { - in: pkix.Name{ - Country: []string{"US"}, - Province: []string{"California"}, - Locality: []string{"Mountain View"}, - Organization: []string{"BoringSSL"}, - }, - out: "c24414d9", - }, - { - in: pkix.Name{ - SerialNumber: "87f4514475ba0a2b", - }, - out: "9dc713cd", - }, - { - in: pkix.Name{ - Country: []string{"US"}, - Province: []string{"California"}, - Locality: []string{"Mountain View"}, - Organization: []string{"Google LLC"}, - OrganizationalUnit: []string{"Production", "campus-sln"}, - CommonName: "Root CA (2021-02-02T07:30:36-08:00)", - }, - out: "0b35a562", - }, - { - in: pkix.Name{ - ExtraNames: []pkix.AttributeTypeAndValue{ - {Type: asn1.ObjectIdentifier{5, 5, 5, 5}, Value: "aaaa"}, - }, - }, - out: "eea339da", - }, - } - for _, tt := range nameTests { - t.Run(tt.in.String(), func(t *testing.T) { - h := x509NameHash(tt.in.ToRDNSequence()) - if h != tt.out { - t.Errorf("x509NameHash(%v): Got %v wanted %v", tt.in, h, tt.out) - } - }) - } -} - -func TestUnsupportedCRLs(t *testing.T) { - crlBytesSomeReasons := []byte(`-----BEGIN X509 CRL----- -MIIEeDCCA2ACAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVVMxHjAcBgNV -BAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczETMBEGA1UEAxMKR1RTIENBIDFPMRcN -MjEwNDI2MTI1OTQxWhcNMjEwNTA2MTE1OTQwWjCCAn0wIgIRAPOOG3L4VLC7CAAA -AABxQgEXDTIxMDQxOTEyMTgxOFowIQIQUK0UwBZkVdQIAAAAAHFCBRcNMjEwNDE5 -MTIxODE4WjAhAhBRIXBJaKoQkQgAAAAAcULHFw0yMTA0MjAxMjE4MTdaMCICEQCv -qQWUq5UxmQgAAAAAcULMFw0yMTA0MjAxMjE4MTdaMCICEQDdv5k1kKwKTQgAAAAA -cUOQFw0yMTA0MjExMjE4MTZaMCICEQDGIEfR8N9sEAgAAAAAcUOWFw0yMTA0MjEx -MjE4MThaMCECEBHgbLXlj5yUCAAAAABxQ/IXDTIxMDQyMTIzMDAyNlowIQIQE1wT -2GGYqKwIAAAAAHFD7xcNMjEwNDIxMjMwMDI5WjAiAhEAo/bSyDjpVtsIAAAAAHFE -txcNMjEwNDIyMjMwMDI3WjAhAhARdCrSrHE0dAgAAAAAcUS/Fw0yMTA0MjIyMzAw -MjhaMCECEHONohfWn3wwCAAAAABxRX8XDTIxMDQyMzIzMDAyOVowIgIRAOYkiUPA -os4vCAAAAABxRYgXDTIxMDQyMzIzMDAyOFowIQIQRNTow5Eg2gEIAAAAAHFGShcN -MjEwNDI0MjMwMDI2WjAhAhBX32dH4/WQ6AgAAAAAcUZNFw0yMTA0MjQyMzAwMjZa -MCICEQDHnUM1vsaP/wgAAAAAcUcQFw0yMTA0MjUyMzAwMjZaMCECEEm5rvmL8sj6 -CAAAAABxRxQXDTIxMDQyNTIzMDAyN1owIQIQW16OQs4YQYkIAAAAAHFIABcNMjEw -NDI2MTI1NDA4WjAhAhAhSohpYsJtDQgAAAAAcUgEFw0yMTA0MjYxMjU0MDlaoGkw -ZzAfBgNVHSMEGDAWgBSY0fhuEOvPm+xgnxiQG6DrfQn9KzALBgNVHRQEBAICBngw -NwYDVR0cAQH/BC0wK6AmoCSGImh0dHA6Ly9jcmwucGtpLmdvb2cvR1RTMU8xY29y -ZS5jcmyBAf8wDQYJKoZIhvcNAQELBQADggEBADPBXbxVxMJ1HC7btXExRUpJHUlU -YbeCZGx6zj5F8pkopbmpV7cpewwhm848Fx4VaFFppZQZd92O08daEC6aEqoug4qF -z6ZrOLzhuKfpW8E93JjgL91v0FYN7iOcT7+ERKCwVEwEkuxszxs7ggW6OJYJNvHh -priIdmcPoiQ3ZrIRH0vE3BfUcNXnKFGATWuDkiRI0I4A5P7NiOf+lAuGZet3/eom -0chgts6sdau10GfeUpHUd4f8e93cS/QeLeG16z7LC8vRLstU3m3vrknpZbdGqSia -97w66mqcnQh9V0swZiEnVLmLufaiuDZJ+6nUzSvLqBlb/ei3T/tKV0BoKJA= ------END X509 CRL-----`) - - crlBytesIndirect := []byte(`-----BEGIN X509 CRL----- -MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU -ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg -Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 -MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG -EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 -MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 -MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV -BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j -BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ -BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG -SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS -TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG -NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq -XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF -6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 -qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 ------END X509 CRL-----`) - - var tests = []struct { - desc string - in []byte - }{ - { - desc: "some reasons", - in: crlBytesSomeReasons, - }, - { - desc: "indirect", - in: crlBytesIndirect, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - crl, err := x509.ParseCRL(tt.in) - if err != nil { - t.Fatal(err) - } - if _, err := parseCRLExtensions(crl); err == nil { - t.Error("expected error got ok") - } - }) - } -} - -func TestCheckCertRevocation(t *testing.T) { - dummyCrlFile := []byte(`-----BEGIN X509 CRL----- -MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU -ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg -Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 -MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG -EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 -MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 -MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV -BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j -BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ -BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG -SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS -TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG -NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq -XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF -6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 -qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 ------END X509 CRL-----`) - crl, err := x509.ParseCRL(dummyCrlFile) - if err != nil { - t.Fatalf("x509.ParseCRL(dummyCrlFile) failed: %v", err) - } - crlExt := &CRL{certList: crl} - var crlIssuer pkix.Name - crlIssuer.FillFromRDNSequence(&crl.TBSCertList.Issuer) - - var revocationTests = []struct { - desc string - in x509.Certificate - revoked RevocationStatus - }{ - { - desc: "Single revoked", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test1", - }, - SerialNumber: big.NewInt(2), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationRevoked, - }, - { - desc: "Revoked no entry issuer", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test1", - }, - SerialNumber: big.NewInt(3), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationRevoked, - }, - { - desc: "Revoked new entry issuer", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test2", - }, - SerialNumber: big.NewInt(4), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationRevoked, - }, - { - desc: "Single unrevoked", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test2", - }, - SerialNumber: big.NewInt(1), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationUnrevoked, - }, - { - desc: "Single unrevoked Issuer", - in: x509.Certificate{ - Issuer: crlIssuer, - SerialNumber: big.NewInt(2), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationUnrevoked, - }, - } - - for _, tt := range revocationTests { - rawIssuer, err := asn1.Marshal(tt.in.Issuer.ToRDNSequence()) - if err != nil { - t.Fatalf("asn1.Marshal(%v) failed: %v", tt.in.Issuer.ToRDNSequence(), err) - } - tt.in.RawIssuer = rawIssuer - t.Run(tt.desc, func(t *testing.T) { - rev, err := checkCertRevocation(&tt.in, crlExt) - if err != nil { - t.Errorf("checkCertRevocation(%v) err = %v", tt.in.Issuer, err) - } else if rev != tt.revoked { - t.Errorf("checkCertRevocation(%v(%v)) returned %v wanted %v", - tt.in.Issuer, tt.in.SerialNumber, rev, tt.revoked) - } - }) - } -} - -func makeChain(t *testing.T, name string) []*x509.Certificate { - t.Helper() - - certChain := make([]*x509.Certificate, 0) - - rest, err := os.ReadFile(name) - if err != nil { - t.Fatalf("os.ReadFile(%v) failed %v", name, err) - } - for len(rest) > 0 { - var block *pem.Block - block, rest = pem.Decode(rest) - c, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Fatalf("ParseCertificate error %v", err) - } - t.Logf("Parsed Cert sub = %v iss = %v", c.Subject, c.Issuer) - certChain = append(certChain, c) - } - return certChain -} - -func loadCRL(t *testing.T, path string) *CRL { - b, err := os.ReadFile(path) - if err != nil { - t.Fatalf("readFile(%v) failed err = %v", path, err) - } - crl, err := x509.ParseCRL(b) - if err != nil { - t.Fatalf("ParseCrl(%v) failed err = %v", path, err) - } - crlExt, err := parseCRLExtensions(crl) - if err != nil { - t.Fatalf("parseCRLExtensions(%v) failed err = %v", path, err) - } - crlExt.rawIssuer, err = extractCRLIssuer(b) - if err != nil { - t.Fatalf("extractCRLIssuer(%v) failed err= %v", path, err) - } - return crlExt -} - -func TestCachedCRL(t *testing.T) { - cache, err := lru.New(5) - if err != nil { - t.Fatalf("lru.New: err = %v", err) - } - - tests := []struct { - desc string - val interface{} - ok bool - }{ - { - desc: "Valid", - val: &CRL{ - certList: &pkix.CertificateList{ - TBSCertList: pkix.TBSCertificateList{ - NextUpdate: time.Now().Add(time.Hour), - }, - }}, - ok: true, - }, - { - desc: "Expired", - val: &CRL{ - certList: &pkix.CertificateList{ - TBSCertList: pkix.TBSCertificateList{ - NextUpdate: time.Now().Add(-time.Hour), - }, - }}, - ok: false, - }, - { - desc: "Wrong Type", - val: "string", - ok: false, - }, - { - desc: "Empty", - val: nil, - ok: false, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if tt.val != nil { - cache.Add(hex.EncodeToString([]byte(tt.desc)), tt.val) - } - _, ok := cachedCrl([]byte(tt.desc), cache) - if tt.ok != ok { - t.Errorf("Cache ok error expected %v vs %v", tt.ok, ok) - } - }) - } -} - -func TestGetIssuerCRLCache(t *testing.T) { - cache, err := lru.New(5) - if err != nil { - t.Fatalf("lru.New: err = %v", err) - } - - tests := []struct { - desc string - rawIssuer []byte - certs []*x509.Certificate - }{ - { - desc: "Valid", - rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - }, - { - desc: "Unverified", - rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, - }, - { - desc: "Not Found", - rawIssuer: []byte("not_found"), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - cache.Purge() - _, err := fetchIssuerCRL(tt.rawIssuer, tt.certs, RevocationConfig{ - RootDir: testdata.Path("."), - Cache: cache, - }) - if err == nil && cache.Len() == 0 { - t.Error("Verified CRL not added to cache") - } - if err != nil && cache.Len() != 0 { - t.Error("Unverified CRL added to cache") - } - }) - } -} - -func TestVerifyCrl(t *testing.T) { - tampered := loadCRL(t, testdata.Path("crl/1.crl")) - // Change the signature so it won't verify - tampered.certList.SignatureValue.Bytes[0]++ - - verifyTests := []struct { - desc string - crl *CRL - certs []*x509.Certificate - cert *x509.Certificate - errWant string - }{ - { - desc: "Pass intermediate", - crl: loadCRL(t, testdata.Path("crl/1.crl")), - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], - errWant: "", - }, - { - desc: "Pass leaf", - crl: loadCRL(t, testdata.Path("crl/2.crl")), - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[2], - errWant: "", - }, - { - desc: "Fail wrong cert chain", - crl: loadCRL(t, testdata.Path("crl/3.crl")), - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/revokedInt.pem"))[1], - errWant: "No certificates mached", - }, - { - desc: "Fail no certs", - crl: loadCRL(t, testdata.Path("crl/1.crl")), - certs: []*x509.Certificate{}, - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], - errWant: "No certificates mached", - }, - { - desc: "Fail Tampered signature", - crl: tampered, - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], - errWant: "verification failure", - }, - } - - for _, tt := range verifyTests { - t.Run(tt.desc, func(t *testing.T) { - err := verifyCRL(tt.crl, tt.cert.RawIssuer, tt.certs) - switch { - case tt.errWant == "" && err != nil: - t.Errorf("Valid CRL did not verify err = %v", err) - case tt.errWant != "" && err == nil: - t.Error("Invalid CRL verified") - case tt.errWant != "" && !strings.Contains(err.Error(), tt.errWant): - t.Errorf("fetchIssuerCRL(_, %v, %v, _) = %v; want Contains(%v)", tt.cert.RawIssuer, tt.certs, err, tt.errWant) - } - }) - } -} - -func TestRevokedCert(t *testing.T) { - revokedIntChain := makeChain(t, testdata.Path("crl/revokedInt.pem")) - revokedLeafChain := makeChain(t, testdata.Path("crl/revokedLeaf.pem")) - validChain := makeChain(t, testdata.Path("crl/unrevoked.pem")) - cache, err := lru.New(5) - if err != nil { - t.Fatalf("lru.New: err = %v", err) - } - - var revocationTests = []struct { - desc string - in tls.ConnectionState - revoked bool - allowUndetermined bool - }{ - { - desc: "Single unrevoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain}}, - revoked: false, - }, - { - desc: "Single revoked intermediate", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedIntChain}}, - revoked: true, - }, - { - desc: "Single revoked leaf", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain}}, - revoked: true, - }, - { - desc: "Multi one revoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, revokedLeafChain}}, - revoked: false, - }, - { - desc: "Multi revoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain, revokedIntChain}}, - revoked: true, - }, - { - desc: "Multi unrevoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, validChain}}, - revoked: false, - }, - { - desc: "Undetermined revoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ - {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, - }}, - revoked: true, - }, - { - desc: "Undetermined allowed", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ - {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, - }}, - revoked: false, - allowUndetermined: true, - }, - } - - for _, tt := range revocationTests { - t.Run(tt.desc, func(t *testing.T) { - err := CheckRevocation(tt.in, RevocationConfig{ - RootDir: testdata.Path("crl"), - AllowUndetermined: tt.allowUndetermined, - Cache: cache, - }) - t.Logf("CheckRevocation err = %v", err) - if tt.revoked && err == nil { - t.Error("Revoked certificate chain was allowed") - } else if !tt.revoked && err != nil { - t.Error("Unrevoked certificate not allowed") - } - }) - } -} - -func setupTLSConn(t *testing.T) (net.Listener, *x509.Certificate, *ecdsa.PrivateKey) { - t.Helper() - templ := x509.Certificate{ - SerialNumber: big.NewInt(5), - BasicConstraintsValid: true, - NotBefore: time.Now().Add(-time.Hour), - NotAfter: time.Now().Add(time.Hour), - IsCA: true, - Subject: pkix.Name{CommonName: "test-cert"}, - KeyUsage: x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - CRLDistributionPoints: []string{"http://static.corp.google.com/crl/campus-sln/borg"}, - } - - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatalf("ecdsa.GenerateKey failed err = %v", err) - } - rawCert, err := x509.CreateCertificate(rand.Reader, &templ, &templ, key.Public(), key) - if err != nil { - t.Fatalf("x509.CreateCertificate failed err = %v", err) - } - cert, err := x509.ParseCertificate(rawCert) - if err != nil { - t.Fatalf("x509.ParseCertificate failed err = %v", err) - } - - srvCfg := tls.Config{ - Certificates: []tls.Certificate{ - { - Certificate: [][]byte{cert.Raw}, - PrivateKey: key, - }, - }, - } - l, err := tls.Listen("tcp6", "[::1]:0", &srvCfg) - if err != nil { - t.Fatalf("tls.Listen failed err = %v", err) - } - return l, cert, key -} - -// TestVerifyConnection will setup a client/server connection and check revocation in the real TLS dialer -func TestVerifyConnection(t *testing.T) { - lis, cert, key := setupTLSConn(t) - defer func() { - lis.Close() - }() - - var handshakeTests = []struct { - desc string - revoked []pkix.RevokedCertificate - success bool - }{ - { - desc: "Empty CRL", - revoked: []pkix.RevokedCertificate{}, - success: true, - }, - { - desc: "Revoked Cert", - revoked: []pkix.RevokedCertificate{ - { - SerialNumber: cert.SerialNumber, - RevocationTime: time.Now(), - }, - }, - success: false, - }, - } - for _, tt := range handshakeTests { - t.Run(tt.desc, func(t *testing.T) { - // Accept one connection. - go func() { - conn, err := lis.Accept() - if err != nil { - t.Errorf("tls.Accept failed err = %v", err) - } else { - conn.Write([]byte("Hello, World!")) - conn.Close() - } - }() - - dir, err := os.MkdirTemp("", "crl_dir") - if err != nil { - t.Fatalf("os.MkdirTemp failed err = %v", err) - } - defer os.RemoveAll(dir) - - crl, err := cert.CreateCRL(rand.Reader, key, tt.revoked, time.Now(), time.Now().Add(time.Hour)) - if err != nil { - t.Fatalf("templ.CreateCRL failed err = %v", err) - } - - err = os.WriteFile(path.Join(dir, fmt.Sprintf("%s.r0", x509NameHash(cert.Subject.ToRDNSequence()))), crl, 0777) - if err != nil { - t.Fatalf("os.WriteFile failed err = %v", err) - } - - cp := x509.NewCertPool() - cp.AddCert(cert) - cliCfg := tls.Config{ - RootCAs: cp, - VerifyConnection: func(cs tls.ConnectionState) error { - return CheckRevocation(cs, RevocationConfig{RootDir: dir}) - }, - } - conn, err := tls.Dial(lis.Addr().Network(), lis.Addr().String(), &cliCfg) - t.Logf("tls.Dial err = %v", err) - if tt.success && err != nil { - t.Errorf("Expected success got err = %v", err) - } - if !tt.success && err == nil { - t.Error("Expected error, but got success") - } - if err == nil { - conn.Close() - } - }) - } -} - -func TestIssuerNonPrintableString(t *testing.T) { - rawIssuer, err := hex.DecodeString("300c310a300806022a030c023a29") - if err != nil { - t.Fatalf("failed to decode issuer: %s", err) - } - _, err = fetchCRL(rawIssuer, RevocationConfig{RootDir: testdata.Path("crl")}) - if err != nil { - t.Fatalf("fetchCRL failed: %s", err) - } -} - -// TestCRLCacheExpirationReloading tests the basic expiration and reloading of a -// cached CRL. The setup places an empty CRL in the cache, and a corresponding -// CRL with a revocation in the CRL directory. We then validate the certificate -// to verify that the certificate is not revoked. Then, we modify the -// NextUpdate time to be in the past so that when we next check for revocation, -// the existing cache entry should be seen as expired, and the CRL in the -// directory showing `revokedInt.pem` as revoked will be loaded, resulting in -// the check returning `RevocationRevoked`. -func TestCRLCacheExpirationReloading(t *testing.T) { - cache, err := lru.New(5) - if err != nil { - t.Fatalf("Creating cache failed") - } - - var certs = makeChain(t, testdata.Path("crl/revokedInt.pem")) - // Certs[1] has the same issuer as the revoked cert - rawIssuer := certs[1].RawIssuer - - // `3.crl`` revokes `revokedInt.pem` - crl := loadCRL(t, testdata.Path("crl/3.crl")) - // Modify the crl so that the cert is NOT revoked and add it to the cache - crl.certList.TBSCertList.RevokedCertificates = nil - crl.certList.TBSCertList.NextUpdate = time.Now().Add(time.Hour) - cache.Add(hex.EncodeToString(rawIssuer), crl) - var cfg = RevocationConfig{RootDir: testdata.Path("crl"), Cache: cache} - revocationStatus := checkChain(certs, cfg) - if revocationStatus != RevocationUnrevoked { - t.Fatalf("Certificate check should be RevocationUnrevoked, was %v", revocationStatus) - } - - // Modify the entry in the cache so that the cache will be refreshed - crl.certList.TBSCertList.NextUpdate = time.Now() - cache.Add(hex.EncodeToString(rawIssuer), crl) - - revocationStatus = checkChain(certs, cfg) - if revocationStatus != RevocationRevoked { - t.Fatalf("A certificate should have been `RevocationRevoked` but was %v", revocationStatus) - } -} From 08188d10eb349ca0e58f473015c8e69a09c79307 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 16 Oct 2023 18:01:41 +0000 Subject: [PATCH 41/62] Revert "Resolve conflicts with upstream - deletion of deprecated crl" This reverts commit e0130640c46efd9a43649bf409c6e762ae66e225, reversing changes made to 21f430135c17da5117b16496103ea74b97a72c4b. Revert deletion --- security/advancedtls/crl_deprecated.go | 521 +++++++++++++ security/advancedtls/crl_deprecated_test.go | 775 ++++++++++++++++++++ 2 files changed, 1296 insertions(+) create mode 100644 security/advancedtls/crl_deprecated.go create mode 100644 security/advancedtls/crl_deprecated_test.go diff --git a/security/advancedtls/crl_deprecated.go b/security/advancedtls/crl_deprecated.go new file mode 100644 index 000000000000..cd7b3d1228ad --- /dev/null +++ b/security/advancedtls/crl_deprecated.go @@ -0,0 +1,521 @@ +// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported +//go:build !go1.19 + +/* + * + * Copyright 2021 gRPC authors. + * + * 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 advancedtls + +import ( + "bytes" + "crypto/sha1" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/binary" + "encoding/hex" + "encoding/pem" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + "time" + + "golang.org/x/crypto/cryptobyte" + cbasn1 "golang.org/x/crypto/cryptobyte/asn1" + "google.golang.org/grpc/grpclog" +) + +var grpclogLogger = grpclog.Component("advancedtls") + +// Cache is an interface to cache CRL files. +// The cache implementation must be concurrency safe. +// A fixed size lru cache from golang-lru is recommended. +type Cache interface { + // Add adds a value to the cache. + Add(key, value interface{}) bool + // Get looks up a key's value from the cache. + Get(key interface{}) (value interface{}, ok bool) +} + +// RevocationConfig contains options for CRL lookup. +type RevocationConfig struct { + // RootDir is the directory to search for CRL files. + // Directory format must match OpenSSL X509_LOOKUP_hash_dir(3). + RootDir string + // AllowUndetermined controls if certificate chains with RevocationUndetermined + // revocation status are allowed to complete. + AllowUndetermined bool + // Cache will store CRL files if not nil, otherwise files are reloaded for every lookup. + Cache Cache +} + +// RevocationStatus is the revocation status for a certificate or chain. +type RevocationStatus int + +const ( + // RevocationUndetermined means we couldn't find or verify a CRL for the cert. + RevocationUndetermined RevocationStatus = iota + // RevocationUnrevoked means we found the CRL for the cert and the cert is not revoked. + RevocationUnrevoked + // RevocationRevoked means we found the CRL and the cert is revoked. + RevocationRevoked +) + +func (s RevocationStatus) String() string { + return [...]string{"RevocationUndetermined", "RevocationUnrevoked", "RevocationRevoked"}[s] +} + +// CRL contains a pkix.CertificateList and parsed +// extensions that aren't provided by the golang CRL parser. +type CRL struct { + CertList *pkix.CertificateList + // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. + AuthorityKeyID []byte + RawIssuer []byte +} + +const tagDirectoryName = 4 + +var ( + // RFC5280, 5.2.4 id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } + oidDeltaCRLIndicator = asn1.ObjectIdentifier{2, 5, 29, 27} + // RFC5280, 5.2.5 id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } + oidIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28} + // RFC5280, 5.3.3 id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } + oidCertificateIssuer = asn1.ObjectIdentifier{2, 5, 29, 29} + // RFC5290, 4.2.1.1 id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } + oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35} +) + +// x509NameHash implements the OpenSSL X509_NAME_hash function for hashed directory lookups. +// +// NOTE: due to the behavior of asn1.Marshal, if the original encoding of the RDN sequence +// contains strings which do not use the ASN.1 PrintableString type, the name will not be +// re-encoded using those types, resulting in a hash which does not match that produced +// by OpenSSL. +func x509NameHash(r pkix.RDNSequence) string { + var canonBytes []byte + // First, canonicalize all the strings. + for _, rdnSet := range r { + for i, rdn := range rdnSet { + value, ok := rdn.Value.(string) + if !ok { + continue + } + // OpenSSL trims all whitespace, does a tolower, and removes extra spaces between words. + // Implemented in x509_name_canon in OpenSSL + canonStr := strings.Join(strings.Fields( + strings.TrimSpace(strings.ToLower(value))), " ") + // Then it changes everything to UTF8 strings + rdnSet[i].Value = asn1.RawValue{Tag: asn1.TagUTF8String, Bytes: []byte(canonStr)} + + } + } + + // Finally, OpenSSL drops the initial sequence tag + // so we marshal all the RDNs separately instead of as a group. + for _, canonRdn := range r { + b, err := asn1.Marshal(canonRdn) + if err != nil { + continue + } + canonBytes = append(canonBytes, b...) + } + + issuerHash := sha1.Sum(canonBytes) + // Openssl takes the first 4 bytes and encodes them as a little endian + // uint32 and then uses the hex to make the file name. + // In C++, this would be: + // (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | + // ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) + // ) & 0xffffffffL; + fileHash := binary.LittleEndian.Uint32(issuerHash[0:4]) + return fmt.Sprintf("%08x", fileHash) +} + +// CheckRevocation checks the connection for revoked certificates based on RFC5280. +// This implementation has the following major limitations: +// - Indirect CRL files are not supported. +// - CRL loading is only supported from directories in the X509_LOOKUP_hash_dir format. +// - OnlySomeReasons is not supported. +// - Delta CRL files are not supported. +// - Certificate CRLDistributionPoint must be URLs, but are then ignored and converted into a file path. +// - CRL checks are done after path building, which goes against RFC4158. +func CheckRevocation(conn tls.ConnectionState, cfg RevocationConfig) error { + return CheckChainRevocation(conn.VerifiedChains, cfg) +} + +// CheckChainRevocation checks the verified certificate chain +// for revoked certificates based on RFC5280. +func CheckChainRevocation(verifiedChains [][]*x509.Certificate, cfg RevocationConfig) error { + // Iterate the verified chains looking for one that is RevocationUnrevoked. + // A single RevocationUnrevoked chain is enough to allow the connection, and a single RevocationRevoked + // chain does not mean the connection should fail. + count := make(map[RevocationStatus]int) + for _, chain := range verifiedChains { + switch checkChain(chain, cfg) { + case RevocationUnrevoked: + // If any chain is RevocationUnrevoked then return no error. + return nil + case RevocationRevoked: + // If this chain is revoked, keep looking for another chain. + count[RevocationRevoked]++ + continue + case RevocationUndetermined: + if cfg.AllowUndetermined { + return nil + } + count[RevocationUndetermined]++ + continue + } + } + return fmt.Errorf("no unrevoked chains found: %v", count) +} + +// checkChain will determine and check all certificates in chain against the CRL +// defined in the certificate with the following rules: +// 1. If any certificate is RevocationRevoked, return RevocationRevoked. +// 2. If any certificate is RevocationUndetermined, return RevocationUndetermined. +// 3. If all certificates are RevocationUnrevoked, return RevocationUnrevoked. +func checkChain(chain []*x509.Certificate, cfg RevocationConfig) RevocationStatus { + chainStatus := RevocationUnrevoked + for _, c := range chain { + switch checkCert(c, chain, cfg) { + case RevocationRevoked: + // Easy case, if a cert in the chain is revoked, the chain is revoked. + return RevocationRevoked + case RevocationUndetermined: + // If we couldn't find the revocation status for a cert, the chain is at best RevocationUndetermined + // keep looking to see if we find a cert in the chain that's RevocationRevoked, + // but return RevocationUndetermined at a minimum. + chainStatus = RevocationUndetermined + case RevocationUnrevoked: + // Continue iterating up the cert chain. + continue + } + } + return chainStatus +} + +func cachedCrl(rawIssuer []byte, cache Cache) (*CRL, bool) { + val, ok := cache.Get(hex.EncodeToString(rawIssuer)) + if !ok { + return nil, false + } + crl, ok := val.(*CRL) + if !ok { + return nil, false + } + // If the CRL is expired, force a reload. + if crl.CertList.HasExpired(time.Now()) { + return nil, false + } + return crl, true +} + +// fetchIssuerCRL fetches and verifies the CRL for rawIssuer from disk or cache if configured in cfg. +func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*CRL, error) { + if cfg.Cache != nil { + if crl, ok := cachedCrl(rawIssuer, cfg.Cache); ok { + return crl, nil + } + } + + crl, err := fetchCRL(rawIssuer, cfg) + if err != nil { + return nil, fmt.Errorf("fetchCRL() failed: %v", err) + } + + if err := verifyCRL(crl, rawIssuer, crlVerifyCrt); err != nil { + return nil, fmt.Errorf("verifyCRL() failed: %v", err) + } + if cfg.Cache != nil { + cfg.Cache.Add(hex.EncodeToString(rawIssuer), crl) + } + return crl, nil +} + +// checkCert checks a single certificate against the CRL defined in the certificate. +// It will fetch and verify the CRL(s) defined in the root directory specified by cfg. +// If we can't load any authoritative CRL files, the status is RevocationUndetermined. +// c is the certificate to check. +// crlVerifyCrt is the group of possible certificates to verify the crl. +func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) RevocationStatus { + crl, err := fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) + if err != nil { + // We couldn't load any CRL files for the certificate, so we don't know if it's RevocationUnrevoked or not. + grpclogLogger.Warningf("getIssuerCRL(%v) err = %v", c.Issuer, err) + return RevocationUndetermined + } + revocation, err := checkCertRevocation(c, crl) + if err != nil { + grpclogLogger.Warningf("checkCertRevocation(CRL %v) failed: %v", crl.CertList.TBSCertList.Issuer, err) + // We couldn't check the CRL file for some reason, so we don't know if it's RevocationUnrevoked or not. + return RevocationUndetermined + } + // Here we've gotten a CRL that loads and verifies. + // We only handle all-reasons CRL files, so this file + // is authoritative for the certificate. + return revocation +} + +func checkCertRevocation(c *x509.Certificate, crl *CRL) (RevocationStatus, error) { + // Per section 5.3.3 we prime the certificate issuer with the CRL issuer. + // Subsequent entries use the previous entry's issuer. + rawEntryIssuer := crl.RawIssuer + + // Loop through all the revoked certificates. + for _, revCert := range crl.CertList.TBSCertList.RevokedCertificates { + // 5.3 Loop through CRL entry extensions for needed information. + for _, ext := range revCert.Extensions { + if oidCertificateIssuer.Equal(ext.Id) { + extIssuer, err := parseCertIssuerExt(ext) + if err != nil { + grpclogLogger.Info(err) + if ext.Critical { + return RevocationUndetermined, err + } + // Since this is a non-critical extension, we can skip it even though + // there was a parsing failure. + continue + } + rawEntryIssuer = extIssuer + } else if ext.Critical { + return RevocationUndetermined, fmt.Errorf("checkCertRevocation: Unhandled critical extension: %v", ext.Id) + } + } + + // If the issuer and serial number appear in the CRL, the certificate is revoked. + if bytes.Equal(c.RawIssuer, rawEntryIssuer) && c.SerialNumber.Cmp(revCert.SerialNumber) == 0 { + // CRL contains the serial, so return revoked. + return RevocationRevoked, nil + } + } + // We did not find the serial in the CRL file that was valid for the cert + // so the certificate is not revoked. + return RevocationUnrevoked, nil +} + +func parseCertIssuerExt(ext pkix.Extension) ([]byte, error) { + // 5.3.3 Certificate Issuer + // CertificateIssuer ::= GeneralNames + // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + var generalNames []asn1.RawValue + if rest, err := asn1.Unmarshal(ext.Value, &generalNames); err != nil || len(rest) != 0 { + return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) + } + + for _, generalName := range generalNames { + // GeneralName ::= CHOICE { + // otherName [0] OtherName, + // rfc822Name [1] IA5String, + // dNSName [2] IA5String, + // x400Address [3] ORAddress, + // directoryName [4] Name, + // ediPartyName [5] EDIPartyName, + // uniformResourceIdentifier [6] IA5String, + // iPAddress [7] OCTET STRING, + // registeredID [8] OBJECT IDENTIFIER } + if generalName.Tag == tagDirectoryName { + return generalName.Bytes, nil + } + } + // Conforming CRL issuers MUST include in this extension the + // distinguished name (DN) from the issuer field of the certificate that + // corresponds to this CRL entry. + // If we couldn't get a directoryName, we can't reason about this file so cert status is + // RevocationUndetermined. + return nil, errors.New("no DN found in certificate issuer") +} + +// RFC 5280, 4.2.1.1 +type authKeyID struct { + ID []byte `asn1:"optional,tag:0"` +} + +// RFC5280, 5.2.5 +// id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } + +// IssuingDistributionPoint ::= SEQUENCE { +// distributionPoint [0] DistributionPointName OPTIONAL, +// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, +// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, +// onlySomeReasons [3] ReasonFlags OPTIONAL, +// indirectCRL [4] BOOLEAN DEFAULT FALSE, +// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } + +// -- at most one of onlyContainsUserCerts, onlyContainsCACerts, +// -- and onlyContainsAttributeCerts may be set to TRUE. +type issuingDistributionPoint struct { + DistributionPoint asn1.RawValue `asn1:"optional,tag:0"` + OnlyContainsUserCerts bool `asn1:"optional,tag:1"` + OnlyContainsCACerts bool `asn1:"optional,tag:2"` + OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"` + IndirectCRL bool `asn1:"optional,tag:4"` + OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"` +} + +// parseCRLExtensions parses the extensions for a CRL +// and checks that they're supported by the parser. +func parseCRLExtensions(c *pkix.CertificateList) (*CRL, error) { + if c == nil { + return nil, errors.New("c is nil, expected any value") + } + certList := &CRL{CertList: c} + + for _, ext := range c.TBSCertList.Extensions { + switch { + case oidDeltaCRLIndicator.Equal(ext.Id): + return nil, fmt.Errorf("delta CRLs unsupported") + + case oidAuthorityKeyIdentifier.Equal(ext.Id): + var a authKeyID + if rest, err := asn1.Unmarshal(ext.Value, &a); err != nil { + return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) + } else if len(rest) != 0 { + return nil, errors.New("trailing data after AKID extension") + } + certList.AuthorityKeyID = a.ID + + case oidIssuingDistributionPoint.Equal(ext.Id): + var dp issuingDistributionPoint + if rest, err := asn1.Unmarshal(ext.Value, &dp); err != nil { + return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) + } else if len(rest) != 0 { + return nil, errors.New("trailing data after IssuingDistributionPoint extension") + } + + if dp.OnlyContainsUserCerts || dp.OnlyContainsCACerts || dp.OnlyContainsAttributeCerts { + return nil, errors.New("CRL only contains some certificate types") + } + if dp.IndirectCRL { + return nil, errors.New("indirect CRLs unsupported") + } + if dp.OnlySomeReasons.BitLength != 0 { + return nil, errors.New("onlySomeReasons unsupported") + } + + case ext.Critical: + return nil, fmt.Errorf("unsupported critical extension: %v", ext.Id) + } + } + + if len(certList.AuthorityKeyID) == 0 { + return nil, errors.New("authority key identifier extension missing") + } + return certList, nil +} + +func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { + var parsedCRL *CRL + // 6.3.3 (a) (1) (ii) + // According to X509_LOOKUP_hash_dir the format is issuer_hash.rN where N is an increasing number. + // There are no gaps, so we break when we can't find a file. + for i := 0; ; i++ { + // Unmarshal to RDNSeqence according to http://go/godoc/crypto/x509/pkix/#Name. + var r pkix.RDNSequence + rest, err := asn1.Unmarshal(rawIssuer, &r) + if len(rest) != 0 || err != nil { + return nil, fmt.Errorf("asn1.Unmarshal(Issuer) len(rest) = %d failed: %v", len(rest), err) + } + crlPath := fmt.Sprintf("%s.r%d", filepath.Join(cfg.RootDir, x509NameHash(r)), i) + crlBytes, err := os.ReadFile(crlPath) + if err != nil { + // Break when we can't read a CRL file. + grpclogLogger.Infof("readFile: %v", err) + break + } + + crl, err := x509.ParseCRL(crlBytes) + if err != nil { + // Parsing errors for a CRL shouldn't happen so fail. + return nil, fmt.Errorf("x509.ParseCrl(%v) failed: %v", crlPath, err) + } + var certList *CRL + if certList, err = parseCRLExtensions(crl); err != nil { + grpclogLogger.Infof("fetchCRL: unsupported crl %v: %v", crlPath, err) + // Continue to find a supported CRL + continue + } + + rawCRLIssuer, err := extractCRLIssuer(crlBytes) + if err != nil { + return nil, err + } + certList.RawIssuer = rawCRLIssuer + // RFC5280, 6.3.3 (b) Verify the issuer and scope of the complete CRL. + if bytes.Equal(rawIssuer, rawCRLIssuer) { + parsedCRL = certList + // Continue to find the highest number in the .rN suffix. + continue + } + } + + if parsedCRL == nil { + return nil, fmt.Errorf("fetchCrls no CRLs found for issuer") + } + return parsedCRL, nil +} + +func verifyCRL(crl *CRL, rawIssuer []byte, chain []*x509.Certificate) error { + // RFC5280, 6.3.3 (f) Obtain and validateate the certification path for the issuer of the complete CRL + // We intentionally limit our CRLs to be signed with the same certificate path as the certificate + // so we can use the chain from the connection. + + for _, c := range chain { + // Use the key where the subject and KIDs match. + // This departs from RFC4158, 3.5.12 which states that KIDs + // cannot eliminate certificates, but RFC5280, 5.2.1 states that + // "Conforming CRL issuers MUST use the key identifier method, and MUST + // include this extension in all CRLs issued." + // So, this is much simpler than RFC4158 and should be compatible. + if bytes.Equal(c.SubjectKeyId, crl.AuthorityKeyID) && bytes.Equal(c.RawSubject, crl.RawIssuer) { + // RFC5280, 6.3.3 (g) Validate signature. + return c.CheckCRLSignature(crl.CertList) + } + } + return fmt.Errorf("verifyCRL: No certificates mached CRL issuer (%v)", crl.CertList.TBSCertList.Issuer) +} + +var crlPemPrefix = []byte("-----BEGIN X509 CRL") + +// extractCRLIssuer extracts the raw ASN.1 encoding of the CRL issuer. Due to the design of +// pkix.CertificateList and pkix.RDNSequence, it is not possible to reliably marshal the +// parsed Issuer to it's original raw encoding. +func extractCRLIssuer(crlBytes []byte) ([]byte, error) { + if bytes.HasPrefix(crlBytes, crlPemPrefix) { + block, _ := pem.Decode(crlBytes) + if block != nil && block.Type == "X509 CRL" { + crlBytes = block.Bytes + } + } + + der := cryptobyte.String(crlBytes) + var issuer cryptobyte.String + if !der.ReadASN1(&der, cbasn1.SEQUENCE) || + !der.ReadASN1(&der, cbasn1.SEQUENCE) || + !der.SkipOptionalASN1(cbasn1.INTEGER) || + !der.SkipASN1(cbasn1.SEQUENCE) || + !der.ReadASN1Element(&issuer, cbasn1.SEQUENCE) { + return nil, errors.New("extractCRLIssuer: invalid ASN.1 encoding") + } + return issuer, nil +} diff --git a/security/advancedtls/crl_deprecated_test.go b/security/advancedtls/crl_deprecated_test.go new file mode 100644 index 000000000000..e16ec7ef2599 --- /dev/null +++ b/security/advancedtls/crl_deprecated_test.go @@ -0,0 +1,775 @@ +// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported +//go:build !go1.19 + +/* + * + * Copyright 2021 gRPC authors. + * + * 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 advancedtls + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "encoding/pem" + "fmt" + "math/big" + "net" + "os" + "path" + "strings" + "testing" + "time" + + lru "github.com/hashicorp/golang-lru" + "google.golang.org/grpc/security/advancedtls/testdata" +) + +func TestX509NameHash(t *testing.T) { + nameTests := []struct { + in pkix.Name + out string + }{ + { + in: pkix.Name{ + Country: []string{"US"}, + Organization: []string{"Example"}, + }, + out: "9cdd41ff", + }, + { + in: pkix.Name{ + Country: []string{"us"}, + Organization: []string{"example"}, + }, + out: "9cdd41ff", + }, + { + in: pkix.Name{ + Country: []string{" us"}, + Organization: []string{"example"}, + }, + out: "9cdd41ff", + }, + { + in: pkix.Name{ + Country: []string{"US"}, + Province: []string{"California"}, + Locality: []string{"Mountain View"}, + Organization: []string{"BoringSSL"}, + }, + out: "c24414d9", + }, + { + in: pkix.Name{ + Country: []string{"US"}, + Province: []string{"California"}, + Locality: []string{"Mountain View"}, + Organization: []string{"BoringSSL"}, + }, + out: "c24414d9", + }, + { + in: pkix.Name{ + SerialNumber: "87f4514475ba0a2b", + }, + out: "9dc713cd", + }, + { + in: pkix.Name{ + Country: []string{"US"}, + Province: []string{"California"}, + Locality: []string{"Mountain View"}, + Organization: []string{"Google LLC"}, + OrganizationalUnit: []string{"Production", "campus-sln"}, + CommonName: "Root CA (2021-02-02T07:30:36-08:00)", + }, + out: "0b35a562", + }, + { + in: pkix.Name{ + ExtraNames: []pkix.AttributeTypeAndValue{ + {Type: asn1.ObjectIdentifier{5, 5, 5, 5}, Value: "aaaa"}, + }, + }, + out: "eea339da", + }, + } + for _, tt := range nameTests { + t.Run(tt.in.String(), func(t *testing.T) { + h := x509NameHash(tt.in.ToRDNSequence()) + if h != tt.out { + t.Errorf("x509NameHash(%v): Got %v wanted %v", tt.in, h, tt.out) + } + }) + } +} + +func TestUnsupportedCRLs(t *testing.T) { + crlBytesSomeReasons := []byte(`-----BEGIN X509 CRL----- +MIIEeDCCA2ACAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVVMxHjAcBgNV +BAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczETMBEGA1UEAxMKR1RTIENBIDFPMRcN +MjEwNDI2MTI1OTQxWhcNMjEwNTA2MTE1OTQwWjCCAn0wIgIRAPOOG3L4VLC7CAAA +AABxQgEXDTIxMDQxOTEyMTgxOFowIQIQUK0UwBZkVdQIAAAAAHFCBRcNMjEwNDE5 +MTIxODE4WjAhAhBRIXBJaKoQkQgAAAAAcULHFw0yMTA0MjAxMjE4MTdaMCICEQCv +qQWUq5UxmQgAAAAAcULMFw0yMTA0MjAxMjE4MTdaMCICEQDdv5k1kKwKTQgAAAAA +cUOQFw0yMTA0MjExMjE4MTZaMCICEQDGIEfR8N9sEAgAAAAAcUOWFw0yMTA0MjEx +MjE4MThaMCECEBHgbLXlj5yUCAAAAABxQ/IXDTIxMDQyMTIzMDAyNlowIQIQE1wT +2GGYqKwIAAAAAHFD7xcNMjEwNDIxMjMwMDI5WjAiAhEAo/bSyDjpVtsIAAAAAHFE +txcNMjEwNDIyMjMwMDI3WjAhAhARdCrSrHE0dAgAAAAAcUS/Fw0yMTA0MjIyMzAw +MjhaMCECEHONohfWn3wwCAAAAABxRX8XDTIxMDQyMzIzMDAyOVowIgIRAOYkiUPA +os4vCAAAAABxRYgXDTIxMDQyMzIzMDAyOFowIQIQRNTow5Eg2gEIAAAAAHFGShcN +MjEwNDI0MjMwMDI2WjAhAhBX32dH4/WQ6AgAAAAAcUZNFw0yMTA0MjQyMzAwMjZa +MCICEQDHnUM1vsaP/wgAAAAAcUcQFw0yMTA0MjUyMzAwMjZaMCECEEm5rvmL8sj6 +CAAAAABxRxQXDTIxMDQyNTIzMDAyN1owIQIQW16OQs4YQYkIAAAAAHFIABcNMjEw +NDI2MTI1NDA4WjAhAhAhSohpYsJtDQgAAAAAcUgEFw0yMTA0MjYxMjU0MDlaoGkw +ZzAfBgNVHSMEGDAWgBSY0fhuEOvPm+xgnxiQG6DrfQn9KzALBgNVHRQEBAICBngw +NwYDVR0cAQH/BC0wK6AmoCSGImh0dHA6Ly9jcmwucGtpLmdvb2cvR1RTMU8xY29y +ZS5jcmyBAf8wDQYJKoZIhvcNAQELBQADggEBADPBXbxVxMJ1HC7btXExRUpJHUlU +YbeCZGx6zj5F8pkopbmpV7cpewwhm848Fx4VaFFppZQZd92O08daEC6aEqoug4qF +z6ZrOLzhuKfpW8E93JjgL91v0FYN7iOcT7+ERKCwVEwEkuxszxs7ggW6OJYJNvHh +priIdmcPoiQ3ZrIRH0vE3BfUcNXnKFGATWuDkiRI0I4A5P7NiOf+lAuGZet3/eom +0chgts6sdau10GfeUpHUd4f8e93cS/QeLeG16z7LC8vRLstU3m3vrknpZbdGqSia +97w66mqcnQh9V0swZiEnVLmLufaiuDZJ+6nUzSvLqBlb/ei3T/tKV0BoKJA= +-----END X509 CRL-----`) + + crlBytesIndirect := []byte(`-----BEGIN X509 CRL----- +MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU +ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg +Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 +MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG +EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 +MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 +MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV +BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j +BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ +BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG +SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS +TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG +NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq +XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF +6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 +qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 +-----END X509 CRL-----`) + + var tests = []struct { + desc string + in []byte + }{ + { + desc: "some reasons", + in: crlBytesSomeReasons, + }, + { + desc: "indirect", + in: crlBytesIndirect, + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + crl, err := x509.ParseCRL(tt.in) + if err != nil { + t.Fatal(err) + } + if _, err := parseCRLExtensions(crl); err == nil { + t.Error("expected error got ok") + } + }) + } +} + +func TestCheckCertRevocation(t *testing.T) { + dummyCrlFile := []byte(`-----BEGIN X509 CRL----- +MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU +ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg +Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 +MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG +EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 +MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 +MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV +BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j +BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ +BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG +SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS +TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG +NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq +XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF +6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 +qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 +-----END X509 CRL-----`) + crl, err := x509.ParseCRL(dummyCrlFile) + if err != nil { + t.Fatalf("x509.ParseCRL(dummyCrlFile) failed: %v", err) + } + crlExt := &CRL{certList: crl} + var crlIssuer pkix.Name + crlIssuer.FillFromRDNSequence(&crl.TBSCertList.Issuer) + + var revocationTests = []struct { + desc string + in x509.Certificate + revoked RevocationStatus + }{ + { + desc: "Single revoked", + in: x509.Certificate{ + Issuer: pkix.Name{ + Country: []string{"USA"}, + Locality: []string{"here"}, + Organization: []string{"us"}, + CommonName: "Test1", + }, + SerialNumber: big.NewInt(2), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationRevoked, + }, + { + desc: "Revoked no entry issuer", + in: x509.Certificate{ + Issuer: pkix.Name{ + Country: []string{"USA"}, + Locality: []string{"here"}, + Organization: []string{"us"}, + CommonName: "Test1", + }, + SerialNumber: big.NewInt(3), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationRevoked, + }, + { + desc: "Revoked new entry issuer", + in: x509.Certificate{ + Issuer: pkix.Name{ + Country: []string{"USA"}, + Locality: []string{"here"}, + Organization: []string{"us"}, + CommonName: "Test2", + }, + SerialNumber: big.NewInt(4), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationRevoked, + }, + { + desc: "Single unrevoked", + in: x509.Certificate{ + Issuer: pkix.Name{ + Country: []string{"USA"}, + Locality: []string{"here"}, + Organization: []string{"us"}, + CommonName: "Test2", + }, + SerialNumber: big.NewInt(1), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationUnrevoked, + }, + { + desc: "Single unrevoked Issuer", + in: x509.Certificate{ + Issuer: crlIssuer, + SerialNumber: big.NewInt(2), + CRLDistributionPoints: []string{"test"}, + }, + revoked: RevocationUnrevoked, + }, + } + + for _, tt := range revocationTests { + rawIssuer, err := asn1.Marshal(tt.in.Issuer.ToRDNSequence()) + if err != nil { + t.Fatalf("asn1.Marshal(%v) failed: %v", tt.in.Issuer.ToRDNSequence(), err) + } + tt.in.RawIssuer = rawIssuer + t.Run(tt.desc, func(t *testing.T) { + rev, err := checkCertRevocation(&tt.in, crlExt) + if err != nil { + t.Errorf("checkCertRevocation(%v) err = %v", tt.in.Issuer, err) + } else if rev != tt.revoked { + t.Errorf("checkCertRevocation(%v(%v)) returned %v wanted %v", + tt.in.Issuer, tt.in.SerialNumber, rev, tt.revoked) + } + }) + } +} + +func makeChain(t *testing.T, name string) []*x509.Certificate { + t.Helper() + + certChain := make([]*x509.Certificate, 0) + + rest, err := os.ReadFile(name) + if err != nil { + t.Fatalf("os.ReadFile(%v) failed %v", name, err) + } + for len(rest) > 0 { + var block *pem.Block + block, rest = pem.Decode(rest) + c, err := x509.ParseCertificate(block.Bytes) + if err != nil { + t.Fatalf("ParseCertificate error %v", err) + } + t.Logf("Parsed Cert sub = %v iss = %v", c.Subject, c.Issuer) + certChain = append(certChain, c) + } + return certChain +} + +func loadCRL(t *testing.T, path string) *CRL { + b, err := os.ReadFile(path) + if err != nil { + t.Fatalf("readFile(%v) failed err = %v", path, err) + } + crl, err := x509.ParseCRL(b) + if err != nil { + t.Fatalf("ParseCrl(%v) failed err = %v", path, err) + } + crlExt, err := parseCRLExtensions(crl) + if err != nil { + t.Fatalf("parseCRLExtensions(%v) failed err = %v", path, err) + } + crlExt.rawIssuer, err = extractCRLIssuer(b) + if err != nil { + t.Fatalf("extractCRLIssuer(%v) failed err= %v", path, err) + } + return crlExt +} + +func TestCachedCRL(t *testing.T) { + cache, err := lru.New(5) + if err != nil { + t.Fatalf("lru.New: err = %v", err) + } + + tests := []struct { + desc string + val interface{} + ok bool + }{ + { + desc: "Valid", + val: &CRL{ + certList: &pkix.CertificateList{ + TBSCertList: pkix.TBSCertificateList{ + NextUpdate: time.Now().Add(time.Hour), + }, + }}, + ok: true, + }, + { + desc: "Expired", + val: &CRL{ + certList: &pkix.CertificateList{ + TBSCertList: pkix.TBSCertificateList{ + NextUpdate: time.Now().Add(-time.Hour), + }, + }}, + ok: false, + }, + { + desc: "Wrong Type", + val: "string", + ok: false, + }, + { + desc: "Empty", + val: nil, + ok: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + if tt.val != nil { + cache.Add(hex.EncodeToString([]byte(tt.desc)), tt.val) + } + _, ok := cachedCrl([]byte(tt.desc), cache) + if tt.ok != ok { + t.Errorf("Cache ok error expected %v vs %v", tt.ok, ok) + } + }) + } +} + +func TestGetIssuerCRLCache(t *testing.T) { + cache, err := lru.New(5) + if err != nil { + t.Fatalf("lru.New: err = %v", err) + } + + tests := []struct { + desc string + rawIssuer []byte + certs []*x509.Certificate + }{ + { + desc: "Valid", + rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + }, + { + desc: "Unverified", + rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, + }, + { + desc: "Not Found", + rawIssuer: []byte("not_found"), + }, + } + + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + cache.Purge() + _, err := fetchIssuerCRL(tt.rawIssuer, tt.certs, RevocationConfig{ + RootDir: testdata.Path("."), + Cache: cache, + }) + if err == nil && cache.Len() == 0 { + t.Error("Verified CRL not added to cache") + } + if err != nil && cache.Len() != 0 { + t.Error("Unverified CRL added to cache") + } + }) + } +} + +func TestVerifyCrl(t *testing.T) { + tampered := loadCRL(t, testdata.Path("crl/1.crl")) + // Change the signature so it won't verify + tampered.certList.SignatureValue.Bytes[0]++ + + verifyTests := []struct { + desc string + crl *CRL + certs []*x509.Certificate + cert *x509.Certificate + errWant string + }{ + { + desc: "Pass intermediate", + crl: loadCRL(t, testdata.Path("crl/1.crl")), + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], + errWant: "", + }, + { + desc: "Pass leaf", + crl: loadCRL(t, testdata.Path("crl/2.crl")), + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[2], + errWant: "", + }, + { + desc: "Fail wrong cert chain", + crl: loadCRL(t, testdata.Path("crl/3.crl")), + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + cert: makeChain(t, testdata.Path("crl/revokedInt.pem"))[1], + errWant: "No certificates mached", + }, + { + desc: "Fail no certs", + crl: loadCRL(t, testdata.Path("crl/1.crl")), + certs: []*x509.Certificate{}, + cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], + errWant: "No certificates mached", + }, + { + desc: "Fail Tampered signature", + crl: tampered, + certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), + cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], + errWant: "verification failure", + }, + } + + for _, tt := range verifyTests { + t.Run(tt.desc, func(t *testing.T) { + err := verifyCRL(tt.crl, tt.cert.RawIssuer, tt.certs) + switch { + case tt.errWant == "" && err != nil: + t.Errorf("Valid CRL did not verify err = %v", err) + case tt.errWant != "" && err == nil: + t.Error("Invalid CRL verified") + case tt.errWant != "" && !strings.Contains(err.Error(), tt.errWant): + t.Errorf("fetchIssuerCRL(_, %v, %v, _) = %v; want Contains(%v)", tt.cert.RawIssuer, tt.certs, err, tt.errWant) + } + }) + } +} + +func TestRevokedCert(t *testing.T) { + revokedIntChain := makeChain(t, testdata.Path("crl/revokedInt.pem")) + revokedLeafChain := makeChain(t, testdata.Path("crl/revokedLeaf.pem")) + validChain := makeChain(t, testdata.Path("crl/unrevoked.pem")) + cache, err := lru.New(5) + if err != nil { + t.Fatalf("lru.New: err = %v", err) + } + + var revocationTests = []struct { + desc string + in tls.ConnectionState + revoked bool + allowUndetermined bool + }{ + { + desc: "Single unrevoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain}}, + revoked: false, + }, + { + desc: "Single revoked intermediate", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedIntChain}}, + revoked: true, + }, + { + desc: "Single revoked leaf", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain}}, + revoked: true, + }, + { + desc: "Multi one revoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, revokedLeafChain}}, + revoked: false, + }, + { + desc: "Multi revoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain, revokedIntChain}}, + revoked: true, + }, + { + desc: "Multi unrevoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, validChain}}, + revoked: false, + }, + { + desc: "Undetermined revoked", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ + {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, + }}, + revoked: true, + }, + { + desc: "Undetermined allowed", + in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ + {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, + }}, + revoked: false, + allowUndetermined: true, + }, + } + + for _, tt := range revocationTests { + t.Run(tt.desc, func(t *testing.T) { + err := CheckRevocation(tt.in, RevocationConfig{ + RootDir: testdata.Path("crl"), + AllowUndetermined: tt.allowUndetermined, + Cache: cache, + }) + t.Logf("CheckRevocation err = %v", err) + if tt.revoked && err == nil { + t.Error("Revoked certificate chain was allowed") + } else if !tt.revoked && err != nil { + t.Error("Unrevoked certificate not allowed") + } + }) + } +} + +func setupTLSConn(t *testing.T) (net.Listener, *x509.Certificate, *ecdsa.PrivateKey) { + t.Helper() + templ := x509.Certificate{ + SerialNumber: big.NewInt(5), + BasicConstraintsValid: true, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour), + IsCA: true, + Subject: pkix.Name{CommonName: "test-cert"}, + KeyUsage: x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + IPAddresses: []net.IP{net.ParseIP("::1")}, + CRLDistributionPoints: []string{"http://static.corp.google.com/crl/campus-sln/borg"}, + } + + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("ecdsa.GenerateKey failed err = %v", err) + } + rawCert, err := x509.CreateCertificate(rand.Reader, &templ, &templ, key.Public(), key) + if err != nil { + t.Fatalf("x509.CreateCertificate failed err = %v", err) + } + cert, err := x509.ParseCertificate(rawCert) + if err != nil { + t.Fatalf("x509.ParseCertificate failed err = %v", err) + } + + srvCfg := tls.Config{ + Certificates: []tls.Certificate{ + { + Certificate: [][]byte{cert.Raw}, + PrivateKey: key, + }, + }, + } + l, err := tls.Listen("tcp6", "[::1]:0", &srvCfg) + if err != nil { + t.Fatalf("tls.Listen failed err = %v", err) + } + return l, cert, key +} + +// TestVerifyConnection will setup a client/server connection and check revocation in the real TLS dialer +func TestVerifyConnection(t *testing.T) { + lis, cert, key := setupTLSConn(t) + defer func() { + lis.Close() + }() + + var handshakeTests = []struct { + desc string + revoked []pkix.RevokedCertificate + success bool + }{ + { + desc: "Empty CRL", + revoked: []pkix.RevokedCertificate{}, + success: true, + }, + { + desc: "Revoked Cert", + revoked: []pkix.RevokedCertificate{ + { + SerialNumber: cert.SerialNumber, + RevocationTime: time.Now(), + }, + }, + success: false, + }, + } + for _, tt := range handshakeTests { + t.Run(tt.desc, func(t *testing.T) { + // Accept one connection. + go func() { + conn, err := lis.Accept() + if err != nil { + t.Errorf("tls.Accept failed err = %v", err) + } else { + conn.Write([]byte("Hello, World!")) + conn.Close() + } + }() + + dir, err := os.MkdirTemp("", "crl_dir") + if err != nil { + t.Fatalf("os.MkdirTemp failed err = %v", err) + } + defer os.RemoveAll(dir) + + crl, err := cert.CreateCRL(rand.Reader, key, tt.revoked, time.Now(), time.Now().Add(time.Hour)) + if err != nil { + t.Fatalf("templ.CreateCRL failed err = %v", err) + } + + err = os.WriteFile(path.Join(dir, fmt.Sprintf("%s.r0", x509NameHash(cert.Subject.ToRDNSequence()))), crl, 0777) + if err != nil { + t.Fatalf("os.WriteFile failed err = %v", err) + } + + cp := x509.NewCertPool() + cp.AddCert(cert) + cliCfg := tls.Config{ + RootCAs: cp, + VerifyConnection: func(cs tls.ConnectionState) error { + return CheckRevocation(cs, RevocationConfig{RootDir: dir}) + }, + } + conn, err := tls.Dial(lis.Addr().Network(), lis.Addr().String(), &cliCfg) + t.Logf("tls.Dial err = %v", err) + if tt.success && err != nil { + t.Errorf("Expected success got err = %v", err) + } + if !tt.success && err == nil { + t.Error("Expected error, but got success") + } + if err == nil { + conn.Close() + } + }) + } +} + +func TestIssuerNonPrintableString(t *testing.T) { + rawIssuer, err := hex.DecodeString("300c310a300806022a030c023a29") + if err != nil { + t.Fatalf("failed to decode issuer: %s", err) + } + _, err = fetchCRL(rawIssuer, RevocationConfig{RootDir: testdata.Path("crl")}) + if err != nil { + t.Fatalf("fetchCRL failed: %s", err) + } +} + +// TestCRLCacheExpirationReloading tests the basic expiration and reloading of a +// cached CRL. The setup places an empty CRL in the cache, and a corresponding +// CRL with a revocation in the CRL directory. We then validate the certificate +// to verify that the certificate is not revoked. Then, we modify the +// NextUpdate time to be in the past so that when we next check for revocation, +// the existing cache entry should be seen as expired, and the CRL in the +// directory showing `revokedInt.pem` as revoked will be loaded, resulting in +// the check returning `RevocationRevoked`. +func TestCRLCacheExpirationReloading(t *testing.T) { + cache, err := lru.New(5) + if err != nil { + t.Fatalf("Creating cache failed") + } + + var certs = makeChain(t, testdata.Path("crl/revokedInt.pem")) + // Certs[1] has the same issuer as the revoked cert + rawIssuer := certs[1].RawIssuer + + // `3.crl`` revokes `revokedInt.pem` + crl := loadCRL(t, testdata.Path("crl/3.crl")) + // Modify the crl so that the cert is NOT revoked and add it to the cache + crl.certList.TBSCertList.RevokedCertificates = nil + crl.certList.TBSCertList.NextUpdate = time.Now().Add(time.Hour) + cache.Add(hex.EncodeToString(rawIssuer), crl) + var cfg = RevocationConfig{RootDir: testdata.Path("crl"), Cache: cache} + revocationStatus := checkChain(certs, cfg) + if revocationStatus != RevocationUnrevoked { + t.Fatalf("Certificate check should be RevocationUnrevoked, was %v", revocationStatus) + } + + // Modify the entry in the cache so that the cache will be refreshed + crl.certList.TBSCertList.NextUpdate = time.Now() + cache.Add(hex.EncodeToString(rawIssuer), crl) + + revocationStatus = checkChain(certs, cfg) + if revocationStatus != RevocationRevoked { + t.Fatalf("A certificate should have been `RevocationRevoked` but was %v", revocationStatus) + } +} From 9b8d07e2bd18de29caf7f6aa45559e14c86e26a7 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Thu, 19 Oct 2023 01:22:10 +0000 Subject: [PATCH 42/62] Update link for gRFC proposal --- security/advancedtls/crl_provider.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 44ca619ce089..96908718c9b5 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -39,7 +39,8 @@ const defaultCRLRefreshDuration = 1 * time.Hour // establishment, so implementations of the CRL function need to be fast, and // slow things such as file IO should be done asynchronously. // TODO(erm-g): Add link to related gRFC once it's ready. -// Please refer to https://github.com/grpc/proposal/ for more details. +// +// [A69: CRL Enhancements]: https://github.com/grpc/proposal/pull/382 type CRLProvider interface { // CRL accepts x509 Cert and returns back related CRL struct. The CRL struct // can be nil, can contain empty or non-empty list of revkoed certificates. @@ -170,7 +171,8 @@ func (p *FileWatcherCRLProvider) Close() { // updating in-memory storage of CRL structs. Please note that the same method is // called periodically by run goroutine. // TODO(erm-g): Add link to related gRFC once it's ready. -// Please refer to https://github.com/grpc/proposal/ for edge case details. +// +// [A69: CRL Enhancements]: https://github.com/grpc/proposal/pull/382 func (p *FileWatcherCRLProvider) ScanCRLDirectory() { dir, err := os.Open(p.opts.CRLDirectory) if err != nil { From ad15e23c433ee3c8e0223ecf33429fe3d47c9e4c Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Thu, 19 Oct 2023 03:17:32 +0000 Subject: [PATCH 43/62] Address PR comments --- security/advancedtls/advancedtls_test.go | 2 +- security/advancedtls/crl_provider.go | 81 +++++++++++------------ security/advancedtls/crl_provider_test.go | 39 ++++++----- security/advancedtls/crl_test.go | 2 +- 4 files changed, 60 insertions(+), 64 deletions(-) diff --git a/security/advancedtls/advancedtls_test.go b/security/advancedtls/advancedtls_test.go index 0893a8159935..77d1a2331a11 100644 --- a/security/advancedtls/advancedtls_test.go +++ b/security/advancedtls/advancedtls_test.go @@ -386,7 +386,7 @@ func (s) TestClientServerHandshake(t *testing.T) { if err != nil { t.Fatalf("readFile(%v) failed err = %v", crlPath, err) } - cRLProvider := MakeStaticCRLProvider([][]byte{rawCRL}) + cRLProvider := NewStaticCRLProvider([][]byte{rawCRL}) return &RevocationConfig{ AllowUndetermined: true, CRLProvider: cRLProvider, diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 96908718c9b5..be81369a475b 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -43,20 +43,19 @@ const defaultCRLRefreshDuration = 1 * time.Hour // [A69: CRL Enhancements]: https://github.com/grpc/proposal/pull/382 type CRLProvider interface { // CRL accepts x509 Cert and returns back related CRL struct. The CRL struct - // can be nil, can contain empty or non-empty list of revkoed certificates. - // Callers are expected to use the returned value as read-only. + // can be nil, can contain empty or non-empty list of revoked certificates. CRL(cert *x509.Certificate) (*CRL, error) } // StaticCRLProvider implements CRLProvider interface by accepting raw content -// of CRL files at creation time and storing parsed CRL structs in-memory. +// of CRL files at creation time and storing parsed CRL structs in-memory. type StaticCRLProvider struct { crls map[string]*CRL } -// MakeStaticCRLProvider processes raw content of CRL files, adds parsed CRL +// NewStaticCRLProvider processes raw content of CRL files, adds parsed CRL // structs into in-memory, and returns a new instance of the StaticCRLProvider. -func MakeStaticCRLProvider(rawCRLs [][]byte) *StaticCRLProvider { +func NewStaticCRLProvider(rawCRLs [][]byte) *StaticCRLProvider { p := StaticCRLProvider{} p.crls = make(map[string]*CRL) for idx, rawCRL := range rawCRLs { @@ -81,64 +80,57 @@ func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { return p.crls[cert.Issuer.ToRDNSequence().String()], nil } -// Options represents a data structure holding a -// configuration for FileWatcherCRLProvider. -type Options struct { +// FileWatcherOptions represents a data structure holding a configuration for +// FileWatcherCRLProvider. +type FileWatcherOptions struct { CRLDirectory string // Path of the directory containing CRL files RefreshDuration time.Duration // Time interval between CRLDirectory scans crlReloadingFailedCallback func(err error) // Custom callback executed when a CRL file can’t be processed } -// FileWatcherCRLProvider implements the CRLProvider interface by periodically scanning -// CRLDirectory (see Options) and storing CRL structs in-memory +// FileWatcherCRLProvider implements the CRLProvider interface by periodically +// scanning CRLDirectory (see FileWatcherOptions) and storing CRL structs +// in-memory. Users should call Close to stop the background refresh of +// CRLDirectory. type FileWatcherCRLProvider struct { - crls map[string]*CRL - opts Options - mu sync.Mutex - done chan struct{} + crls map[string]*CRL + opts FileWatcherOptions + mu sync.Mutex + scanMutex sync.Mutex + stop chan struct{} + done chan struct{} } -// MakeFileWatcherCRLProvider returns a new instance of the -// FileWatcherCRLProvider. It uses Options to validate and apply configuration -// required for creating a new instance. -func MakeFileWatcherCRLProvider(o Options) (*FileWatcherCRLProvider, error) { +// NewFileWatcherCRLProvider returns a new instance of the +// FileWatcherCRLProvider. It uses FileWatcherOptions to validate and apply +// configuration required for creating a new instance. Users should call Close +// to stop the background refresh of CRLDirectory. +func NewFileWatcherCRLProvider(o FileWatcherOptions) (*FileWatcherCRLProvider, error) { if err := o.validate(); err != nil { return nil, err } done := make(chan struct{}) + stop := make(chan struct{}) provider := &FileWatcherCRLProvider{ crls: make(map[string]*CRL), opts: o, + stop: stop, done: done, } go provider.run() return provider, nil } -func (o *Options) validate() error { +func (o *FileWatcherOptions) validate() error { // Checks relates to CRLDirectory. if o.CRLDirectory == "" { return fmt.Errorf("advancedtls: CRLDirectory needs to be specified") } - fileInfo, err := os.Stat(o.CRLDirectory) - if err != nil { - if os.IsNotExist(err) { - return fmt.Errorf("advancedtls: CRLDirectory %v does not exist", o.CRLDirectory) - } - return err - } - if !fileInfo.IsDir() { - return fmt.Errorf("advancedtls: CRLDirectory %v is not a directory", o.CRLDirectory) - } - _, err = os.Open(o.CRLDirectory) - if err != nil { - if os.IsPermission(err) { - return fmt.Errorf("advancedtls: CRLDirectory %v is not readable", o.CRLDirectory) - } - return err + if _, err := os.ReadDir(o.CRLDirectory); err != nil { + return fmt.Errorf("advancedtls: CRLDirectory %v is not readable: %v", o.CRLDirectory, err) } // Checks related to RefreshDuration. - if o.RefreshDuration <= 0 || o.RefreshDuration < time.Second { + if o.RefreshDuration < time.Second { o.RefreshDuration = defaultCRLRefreshDuration grpclogLogger.Warningf("RefreshDuration must larger then 1 second: provided value %v, default value will be used %v", o.RefreshDuration, defaultCRLRefreshDuration) } @@ -147,13 +139,14 @@ func (o *Options) validate() error { // Start starts watching the directory for CRL files and updates the provider accordingly. func (p *FileWatcherCRLProvider) run() { + defer close(p.done) ticker := time.NewTicker(p.opts.RefreshDuration) defer ticker.Stop() p.ScanCRLDirectory() for { select { - case <-p.done: + case <-p.stop: ticker.Stop() return case <-ticker.C: @@ -162,18 +155,22 @@ func (p *FileWatcherCRLProvider) run() { } } -// Close stops the background refresh of CRLDirectory of FileWatcherCRLProvider +// Close stops the background refresh of CRLDirectory of FileWatcherCRLProvider. func (p *FileWatcherCRLProvider) Close() { - close(p.done) + close(p.stop) + <-p.done } -// ScanCRLDirectory starts the process of scanning Options.CRLDirectory and -// updating in-memory storage of CRL structs. Please note that the same method is -// called periodically by run goroutine. +// ScanCRLDirectory starts the process of scanning +// FileWatcherOptions.CRLDirectory and updating in-memory storage of CRL +// structs. Please note that the same method is called periodically by run +// goroutine. // TODO(erm-g): Add link to related gRFC once it's ready. // // [A69: CRL Enhancements]: https://github.com/grpc/proposal/pull/382 func (p *FileWatcherCRLProvider) ScanCRLDirectory() { + p.scanMutex.Lock() + defer p.scanMutex.Unlock() dir, err := os.Open(p.opts.CRLDirectory) if err != nil { grpclogLogger.Errorf("Can't open CRLDirectory %v", p.opts.CRLDirectory, err) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 1a3c93fad722..4a38eb248e45 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -31,8 +31,6 @@ import ( "google.golang.org/grpc/security/advancedtls/testdata" ) -const nonCRLFilesUnderCRLDirectory = 5 - // TestStaticCRLProvider tests how StaticCRLProvider handles the major four // cases for CRL checks. It loads the CRLs under crl directory, constructs // unrevoked, revoked leaf, and revoked intermediate chains, as well as a chain @@ -46,7 +44,7 @@ func (s) TestStaticCRLProvider(t *testing.T) { } rawCRLs = append(rawCRLs, rawCRL) } - p := MakeStaticCRLProvider(rawCRLs) + p := NewStaticCRLProvider(rawCRLs) // Each test data entry contains a description of a certificate chain, // certificate chain itself, and if CRL is not expected to be found. tests := []struct { @@ -89,30 +87,30 @@ func (s) TestStaticCRLProvider(t *testing.T) { } // TestFileWatcherCRLProviderConfig checks creation of FileWatcherCRLProvider, -// and the validation of Options configuration. The configurations include empty +// and the validation of FileWatcherOptions configuration. The configurations include empty // one, non existing CRLDirectory, invalid RefreshDuration, and the correct one. func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { - if _, err := MakeFileWatcherCRLProvider(Options{}); err == nil { - t.Fatalf("Empty Options should not be allowed") + if _, err := NewFileWatcherCRLProvider(FileWatcherOptions{}); err == nil { + t.Fatalf("Empty FileWatcherOptions should not be allowed") } - if _, err := MakeFileWatcherCRLProvider(Options{CRLDirectory: "I_do_not_exist"}); err == nil { + if _, err := NewFileWatcherCRLProvider(FileWatcherOptions{CRLDirectory: "I_do_not_exist"}); err == nil { t.Fatalf("CRLDirectory must exist") } - if defaultProvider, err := MakeFileWatcherCRLProvider(Options{CRLDirectory: testdata.Path("crl/provider")}); err == nil { - if defaultProvider.opts.RefreshDuration != defaultCRLRefreshDuration { - t.Fatalf("RefreshDuration is not properly updated by validate() func") - } - defaultProvider.Close() - } else { + defaultProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{CRLDirectory: testdata.Path("crl/provider")}) + if err != nil { t.Fatal("Unexpected error:", err) } + if defaultProvider.opts.RefreshDuration != defaultCRLRefreshDuration { + t.Fatalf("RefreshDuration is not properly updated by validate() func") + } + defaultProvider.Close() customCallback := func(err error) { fmt.Printf("Custom error message: %v", err) } - regularProvider, err := MakeFileWatcherCRLProvider(Options{ + regularProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{ CRLDirectory: testdata.Path("crl"), - RefreshDuration: 5 * time.Second, + RefreshDuration: 1 * time.Hour, crlReloadingFailedCallback: customCallback, }) if err != nil { @@ -128,13 +126,14 @@ func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { // that it’s correctly processed. Additionally, we also check if number of // invocations of custom callback is correct. func (s) TestFileWatcherCRLProvider(t *testing.T) { + const nonCRLFilesUnderCRLDirectory = 5 nonCRLFilesSet := make(map[string]struct{}) customCallback := func(err error) { nonCRLFilesSet[err.Error()] = struct{}{} } - p, err := MakeFileWatcherCRLProvider(Options{ + p, err := NewFileWatcherCRLProvider(FileWatcherOptions{ CRLDirectory: testdata.Path("crl"), - RefreshDuration: 5 * time.Second, + RefreshDuration: 1 * time.Hour, crlReloadingFailedCallback: customCallback, }) if err != nil { @@ -188,15 +187,15 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { } // TestFileWatcherCRLProviderDirectoryScan tests how FileWatcherCRLProvider -// handles different contents of Options.CRLDirectory. +// handles different contents of FileWatcherOptions.CRLDirectory. // We update the content with various (correct and incorrect) CRL files and // check if in-memory storage was properly updated. Please note that the same // instance of FileWatcherCRLProvider is used for the whole test so test cases -// cases are not independent from each other. +// are not independent from each other. func (s) TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { sourcePath := testdata.Path("crl") targetPath := testdata.Path("crl/provider/filewatcher") - p, err := MakeFileWatcherCRLProvider(Options{ + p, err := NewFileWatcherCRLProvider(FileWatcherOptions{ CRLDirectory: targetPath, RefreshDuration: 1 * time.Hour, }) diff --git a/security/advancedtls/crl_test.go b/security/advancedtls/crl_test.go index e6796b666372..b9ea2681484e 100644 --- a/security/advancedtls/crl_test.go +++ b/security/advancedtls/crl_test.go @@ -517,7 +517,7 @@ func TestRevokedCert(t *testing.T) { } rawCRLs = append(rawCRLs, rawCRL) } - cRLProvider := MakeStaticCRLProvider(rawCRLs) + cRLProvider := NewStaticCRLProvider(rawCRLs) if err != nil { t.Fatalf("lru.New: err = %v", err) } From 8e02546f4c759c0e1f1cac19752e91a8fe26cc64 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Fri, 20 Oct 2023 02:49:43 +0000 Subject: [PATCH 44/62] Address PR comments part 1 --- security/advancedtls/crl_provider.go | 14 ++-- .../testdata/crl/provider/README.md | 8 +- .../testdata/crl/provider/create.sh | 76 +++++++++++++++++++ .../advancedtls/testdata/crl/provider/crl.cnf | 1 - .../testdata/crl/provider/extensions.conf | 2 +- 5 files changed, 88 insertions(+), 13 deletions(-) create mode 100755 security/advancedtls/testdata/crl/provider/create.sh diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index be81369a475b..47ddf47e450e 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -35,10 +35,9 @@ const defaultCRLRefreshDuration = 1 * time.Hour // but doesn't prescribe a specific way to load and store CRLs. Such // implementations can be used in RevocationConfig of advancedtls.ClientOptions // and/or advancedtls.ServerOptions. -// Please note that checking CRLs is being directly on the path of connection +// Please note that checking CRLs is directly on the path of connection // establishment, so implementations of the CRL function need to be fast, and // slow things such as file IO should be done asynchronously. -// TODO(erm-g): Add link to related gRFC once it's ready. // // [A69: CRL Enhancements]: https://github.com/grpc/proposal/pull/382 type CRLProvider interface { @@ -84,7 +83,7 @@ func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { // FileWatcherCRLProvider. type FileWatcherOptions struct { CRLDirectory string // Path of the directory containing CRL files - RefreshDuration time.Duration // Time interval between CRLDirectory scans + RefreshDuration time.Duration // Time interval between CRLDirectory scans, can't be smaller than 1 second crlReloadingFailedCallback func(err error) // Custom callback executed when a CRL file can’t be processed } @@ -163,9 +162,8 @@ func (p *FileWatcherCRLProvider) Close() { // ScanCRLDirectory starts the process of scanning // FileWatcherOptions.CRLDirectory and updating in-memory storage of CRL -// structs. Please note that the same method is called periodically by run -// goroutine. -// TODO(erm-g): Add link to related gRFC once it's ready. +// structs. Users should not call this function in a loop since it's called +// periodically (see FileWatcherOptions.RefreshDuration) by run goroutine. // // [A69: CRL Enhancements]: https://github.com/grpc/proposal/pull/382 func (p *FileWatcherCRLProvider) ScanCRLDirectory() { @@ -206,7 +204,7 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { successCounter++ } // Only if all the files are processed successful we can swap maps (there - // might be deletions of entries in this case + // might be deletions of entries in this case). if len(files) == successCounter { p.mu.Lock() defer p.mu.Unlock() @@ -216,7 +214,7 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { } grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files found and processed successfully, in-memory CRL storage flushed and repopulated", p.opts.CRLDirectory, len(files)) } else { - // Since some of the files failed we can only add/update entries in the map + // Since some of the files failed we can only add/update entries in the map. p.mu.Lock() defer p.mu.Unlock() for key, value := range tempCRLs { diff --git a/security/advancedtls/testdata/crl/provider/README.md b/security/advancedtls/testdata/crl/provider/README.md index 26d53aec0ee0..ce916297d3f7 100644 --- a/security/advancedtls/testdata/crl/provider/README.md +++ b/security/advancedtls/testdata/crl/provider/README.md @@ -1,11 +1,13 @@ About This Directory ------------- -The directory test data (certificates and CRLs) used for testing CRL providers -functionality. +The directory contains test data (certificates and CRLs) used for testing CRL +providers functionality. How to Generate Test Data Using OpenSSL ------------- +To generate test data please follow the steps below or run create.sh script. + We need to generate the following artifacts for testing CRL provider: * server self signed CA cert * client self signed CA cert @@ -45,7 +47,7 @@ For CRL generation we need 2 more files called `index.txt` and `crlnumber.txt`: $ echo "1000" > crlnumber.txt $ touch index.txt ``` -Also we need anothe config `crl.cnf` - +Also we need another config `crl.cnf` - ``` [ ca ] default_ca = my_ca diff --git a/security/advancedtls/testdata/crl/provider/create.sh b/security/advancedtls/testdata/crl/provider/create.sh new file mode 100755 index 000000000000..6f1fbbece0a5 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider/create.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# The script contains a sequence of commands described in README.md +openssl req -x509 \ + -newkey rsa:4096 \ + -keyout server_trust_key.pem \ + -out server_trust_cert.pem \ + -days 365 \ + -subj "/C=US/ST=VA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.ca.com" \ + -nodes + +openssl req -x509 \ + -newkey rsa:4096 \ + -keyout client_trust_key.pem \ + -out client_trust_cert.pem \ + -days 365 \ + -subj "/C=US/ST=CA/L=SVL/O=Internet Widgits Pty Ltd" \ + -nodes + +openssl req -newkey rsa:4096 \ + -keyout server_cert.key \ + -out new_cert.csr \ + -nodes \ + -subj "/C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com" \ + -sha256 + +openssl x509 -req \ + -in new_cert.csr \ + -out server_cert.pem \ + -CA client_trust_cert.pem \ + -CAkey client_trust_key.pem \ + -CAcreateserial \ + -days 3650 \ + -sha256 \ + -extfile extensions.conf + +openssl req -newkey rsa:4096 \ + -keyout client_cert.key \ + -out new_cert.csr \ + -nodes \ + -subj "/C=US/ST=CA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.com" \ + -sha256 + +openssl x509 -req \ + -in new_cert.csr \ + -out client_cert.pem \ + -CA server_trust_cert.pem \ + -CAkey server_trust_key.pem \ + -CAcreateserial \ + -days 3650 \ + -sha256 \ + -extfile extensions.conf + +echo "1000" > crlnumber.txt + +touch index.txt + +openssl ca -gencrl \ + -keyfile client_trust_key.pem \ + -cert client_trust_cert.pem \ + -out crl_empty.pem \ + -config crl.cnf + +openssl ca -revoke server_cert.pem \ + -keyfile client_trust_key.pem \ + -cert client_trust_cert.pem \ + -config crl.cnf + +openssl ca -gencrl \ + -keyfile client_trust_key.pem \ + -cert client_trust_cert.pem \ + -out crl_server_revoked.pem \ + -config crl.cnf + + +rm *_csr.pem diff --git a/security/advancedtls/testdata/crl/provider/crl.cnf b/security/advancedtls/testdata/crl/provider/crl.cnf index 8d0124557043..55d7e73c7d51 100644 --- a/security/advancedtls/testdata/crl/provider/crl.cnf +++ b/security/advancedtls/testdata/crl/provider/crl.cnf @@ -13,4 +13,3 @@ crl_extensions = crl_ext [crl_ext] # Authority Key Identifier extension authorityKeyIdentifier=keyid:always,issuer:always - diff --git a/security/advancedtls/testdata/crl/provider/extensions.conf b/security/advancedtls/testdata/crl/provider/extensions.conf index 8edcc568806b..b414918a9e4b 100644 --- a/security/advancedtls/testdata/crl/provider/extensions.conf +++ b/security/advancedtls/testdata/crl/provider/extensions.conf @@ -1,4 +1,4 @@ subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer basicConstraints = CA:FALSE -keyUsage = digitalSignature, keyEncipherment \ No newline at end of file +keyUsage = digitalSignature, keyEncipherment From e6a690df3f4b415163d0017f089584bc054e1d60 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Fri, 20 Oct 2023 05:09:58 +0000 Subject: [PATCH 45/62] Address PR comments part 2 --- security/advancedtls/crl_provider.go | 9 +++------ security/advancedtls/crl_provider_test.go | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 47ddf47e450e..50b1cdf199f3 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -146,7 +146,7 @@ func (p *FileWatcherCRLProvider) run() { for { select { case <-p.stop: - ticker.Stop() + grpclogLogger.Infof("Scanning of CRLDirectory %v stopped", p.opts.CRLDirectory) return case <-ticker.C: p.ScanCRLDirectory() @@ -203,15 +203,12 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { tempCRLs[crl.certList.Issuer.ToRDNSequence().String()] = crl successCounter++ } - // Only if all the files are processed successful we can swap maps (there + // Only if all the files are processed successfully we can swap maps (there // might be deletions of entries in this case). if len(files) == successCounter { p.mu.Lock() defer p.mu.Unlock() - p.crls = make(map[string]*CRL) - for key, value := range tempCRLs { - p.crls[key] = value - } + p.crls = tempCRLs grpclogLogger.Infof("Scan of CRLDirectory %v completed, %v files found and processed successfully, in-memory CRL storage flushed and repopulated", p.opts.CRLDirectory, len(files)) } else { // Since some of the files failed we can only add/update entries in the map. diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 4a38eb248e45..35afbab3dd88 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -194,7 +194,7 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { // are not independent from each other. func (s) TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { sourcePath := testdata.Path("crl") - targetPath := testdata.Path("crl/provider/filewatcher") + targetPath := createTmpDir(t) p, err := NewFileWatcherCRLProvider(FileWatcherOptions{ CRLDirectory: targetPath, RefreshDuration: 1 * time.Hour, @@ -294,3 +294,16 @@ func copyFiles(sourcePath string, targetPath string, fileNames []string, t *test } } } + +func createTmpDir(t *testing.T) string { + t.Helper() + + // Create a temp directory. Passing an empty string for the first argument + // uses the system temp directory. + dir, err := os.MkdirTemp("", "filewatcher*") + if err != nil { + t.Fatalf("os.MkdirTemp() failed: %v", err) + } + t.Logf("Using tmpdir: %s", dir) + return dir +} From 340757d28e4713ac7dab71cb45376584b6ca67db Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Fri, 20 Oct 2023 18:53:36 +0000 Subject: [PATCH 46/62] Address PR comments part 3 --- security/advancedtls/advancedtls_test.go | 4 +- security/advancedtls/crl_provider.go | 16 ++-- security/advancedtls/crl_provider_test.go | 6 +- .../internal/testutils/testutils.go | 8 +- security/advancedtls/testdata/crl/README.md | 71 +++++++++++++++++ .../testdata/crl/provider/README.md | 75 ------------------ .../testdata/crl/provider/client_cert.key | 52 ------------- .../testdata/crl/provider/client_cert.pem | 32 -------- .../crl/provider/client_trust_cert.pem | 32 -------- .../crl/provider/client_trust_key.pem | 52 ------------- .../testdata/crl/provider/create.sh | 76 ------------------- .../testdata/crl/provider/crl_empty.pem | 20 ----- .../crl/provider/crl_server_revoked.pem | 20 ----- .../testdata/crl/provider/crlnumber.txt | 1 - .../testdata/crl/provider/index.txt | 1 - .../testdata/crl/provider/server_cert.key | 52 ------------- .../testdata/crl/provider/server_cert.pem | 32 -------- .../crl/provider/server_trust_cert.pem | 32 -------- .../crl/provider/server_trust_key.pem | 52 ------------- .../testdata/crl/provider_client_cert.key | 52 +++++++++++++ .../testdata/crl/provider_client_cert.pem | 32 ++++++++ .../crl/provider_client_trust_cert.pem | 32 ++++++++ .../crl/provider_client_trust_key.pem | 52 +++++++++++++ .../testdata/crl/provider_create.sh | 76 +++++++++++++++++++ .../{provider/crl.cnf => provider_crl.cnf} | 5 +- .../testdata/crl/provider_crl_empty.pem | 20 +++++ .../crl/provider_crl_server_revoked.pem | 21 +++++ ...tensions.conf => provider_extensions.conf} | 0 .../testdata/crl/provider_server_cert.key | 52 +++++++++++++ .../testdata/crl/provider_server_cert.pem | 32 ++++++++ .../crl/provider_server_trust_cert.pem | 32 ++++++++ .../crl/provider_server_trust_key.pem | 52 +++++++++++++ 32 files changed, 543 insertions(+), 549 deletions(-) delete mode 100644 security/advancedtls/testdata/crl/provider/README.md delete mode 100644 security/advancedtls/testdata/crl/provider/client_cert.key delete mode 100644 security/advancedtls/testdata/crl/provider/client_cert.pem delete mode 100644 security/advancedtls/testdata/crl/provider/client_trust_cert.pem delete mode 100644 security/advancedtls/testdata/crl/provider/client_trust_key.pem delete mode 100755 security/advancedtls/testdata/crl/provider/create.sh delete mode 100644 security/advancedtls/testdata/crl/provider/crl_empty.pem delete mode 100644 security/advancedtls/testdata/crl/provider/crl_server_revoked.pem delete mode 100644 security/advancedtls/testdata/crl/provider/crlnumber.txt delete mode 100644 security/advancedtls/testdata/crl/provider/index.txt delete mode 100644 security/advancedtls/testdata/crl/provider/server_cert.key delete mode 100644 security/advancedtls/testdata/crl/provider/server_cert.pem delete mode 100644 security/advancedtls/testdata/crl/provider/server_trust_cert.pem delete mode 100644 security/advancedtls/testdata/crl/provider/server_trust_key.pem create mode 100644 security/advancedtls/testdata/crl/provider_client_cert.key create mode 100644 security/advancedtls/testdata/crl/provider_client_cert.pem create mode 100644 security/advancedtls/testdata/crl/provider_client_trust_cert.pem create mode 100644 security/advancedtls/testdata/crl/provider_client_trust_key.pem create mode 100755 security/advancedtls/testdata/crl/provider_create.sh rename security/advancedtls/testdata/crl/{provider/crl.cnf => provider_crl.cnf} (77%) create mode 100644 security/advancedtls/testdata/crl/provider_crl_empty.pem create mode 100644 security/advancedtls/testdata/crl/provider_crl_server_revoked.pem rename security/advancedtls/testdata/crl/{provider/extensions.conf => provider_extensions.conf} (100%) create mode 100644 security/advancedtls/testdata/crl/provider_server_cert.key create mode 100644 security/advancedtls/testdata/crl/provider_server_cert.pem create mode 100644 security/advancedtls/testdata/crl/provider_server_trust_cert.pem create mode 100644 security/advancedtls/testdata/crl/provider_server_trust_key.pem diff --git a/security/advancedtls/advancedtls_test.go b/security/advancedtls/advancedtls_test.go index 77d1a2331a11..ce176df0bf35 100644 --- a/security/advancedtls/advancedtls_test.go +++ b/security/advancedtls/advancedtls_test.go @@ -735,7 +735,7 @@ func (s) TestClientServerHandshake(t *testing.T) { clientGetRoot: getRootCAsForClientCRL, clientVerifyFunc: clientVerifyFuncGood, clientVType: CertVerification, - clientRevocationConfig: makeStaticCRLProvider(testdata.Path("crl/provider/crl_empty.pem")), + clientRevocationConfig: makeStaticCRLProvider(testdata.Path("crl/provider_crl_empty.pem")), serverMutualTLS: true, serverCert: []tls.Certificate{cs.ServerCert3}, serverGetRoot: getRootCAsForServerCRL, @@ -750,7 +750,7 @@ func (s) TestClientServerHandshake(t *testing.T) { clientGetRoot: getRootCAsForClientCRL, clientVerifyFunc: clientVerifyFuncGood, clientVType: CertVerification, - clientRevocationConfig: makeStaticCRLProvider(testdata.Path("crl/provider/crl_server_revoked.pem")), + clientRevocationConfig: makeStaticCRLProvider(testdata.Path("crl/provider_crl_server_revoked.pem")), serverMutualTLS: true, serverCert: []tls.Certificate{cs.ServerCert3}, serverGetRoot: getRootCAsForServerCRL, diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 50b1cdf199f3..c69f239951ba 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -84,7 +84,7 @@ func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { type FileWatcherOptions struct { CRLDirectory string // Path of the directory containing CRL files RefreshDuration time.Duration // Time interval between CRLDirectory scans, can't be smaller than 1 second - crlReloadingFailedCallback func(err error) // Custom callback executed when a CRL file can’t be processed + CRLReloadingFailedCallback func(err error) // Custom callback executed when a CRL file can’t be processed } // FileWatcherCRLProvider implements the CRLProvider interface by periodically @@ -129,7 +129,7 @@ func (o *FileWatcherOptions) validate() error { return fmt.Errorf("advancedtls: CRLDirectory %v is not readable: %v", o.CRLDirectory, err) } // Checks related to RefreshDuration. - if o.RefreshDuration < time.Second { + if o.RefreshDuration < time.Minute { o.RefreshDuration = defaultCRLRefreshDuration grpclogLogger.Warningf("RefreshDuration must larger then 1 second: provided value %v, default value will be used %v", o.RefreshDuration, defaultCRLRefreshDuration) } @@ -172,8 +172,8 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { dir, err := os.Open(p.opts.CRLDirectory) if err != nil { grpclogLogger.Errorf("Can't open CRLDirectory %v", p.opts.CRLDirectory, err) - if p.opts.crlReloadingFailedCallback != nil { - p.opts.crlReloadingFailedCallback(err) + if p.opts.CRLReloadingFailedCallback != nil { + p.opts.CRLReloadingFailedCallback(err) } } defer dir.Close() @@ -181,8 +181,8 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { files, err := dir.ReadDir(0) if err != nil { grpclogLogger.Errorf("Can't access files under CRLDirectory %v", p.opts.CRLDirectory, err) - if p.opts.crlReloadingFailedCallback != nil { - p.opts.crlReloadingFailedCallback(err) + if p.opts.CRLReloadingFailedCallback != nil { + p.opts.CRLReloadingFailedCallback(err) } } @@ -195,8 +195,8 @@ func (p *FileWatcherCRLProvider) ScanCRLDirectory() { if err != nil { failCounter++ grpclogLogger.Warningf("Can't add CRL from file %v under CRLDirectory %v", filePath, p.opts.CRLDirectory, err) - if p.opts.crlReloadingFailedCallback != nil { - p.opts.crlReloadingFailedCallback(err) + if p.opts.CRLReloadingFailedCallback != nil { + p.opts.CRLReloadingFailedCallback(err) } continue } diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 35afbab3dd88..44f73ccb868a 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -96,7 +96,7 @@ func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { if _, err := NewFileWatcherCRLProvider(FileWatcherOptions{CRLDirectory: "I_do_not_exist"}); err == nil { t.Fatalf("CRLDirectory must exist") } - defaultProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{CRLDirectory: testdata.Path("crl/provider")}) + defaultProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{CRLDirectory: testdata.Path("crl")}) if err != nil { t.Fatal("Unexpected error:", err) } @@ -111,7 +111,7 @@ func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { regularProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{ CRLDirectory: testdata.Path("crl"), RefreshDuration: 1 * time.Hour, - crlReloadingFailedCallback: customCallback, + CRLReloadingFailedCallback: customCallback, }) if err != nil { t.Fatal("Unexpected error while creating regular FileWatcherCRLProvider:", err) @@ -134,7 +134,7 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { p, err := NewFileWatcherCRLProvider(FileWatcherOptions{ CRLDirectory: testdata.Path("crl"), RefreshDuration: 1 * time.Hour, - crlReloadingFailedCallback: customCallback, + CRLReloadingFailedCallback: customCallback, }) if err != nil { t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) diff --git a/security/advancedtls/internal/testutils/testutils.go b/security/advancedtls/internal/testutils/testutils.go index 722a72ca108b..1ad272b4f846 100644 --- a/security/advancedtls/internal/testutils/testutils.go +++ b/security/advancedtls/internal/testutils/testutils.go @@ -89,7 +89,7 @@ func (cs *CertStore) LoadCerts() error { if cs.ClientCert2, err = tls.LoadX509KeyPair(testdata.Path("client_cert_2.pem"), testdata.Path("client_key_2.pem")); err != nil { return err } - if cs.ClientCert3, err = tls.LoadX509KeyPair(testdata.Path("crl/provider/client_cert.pem"), testdata.Path("crl/provider/client_cert.key")); err != nil { + if cs.ClientCert3, err = tls.LoadX509KeyPair(testdata.Path("crl/provider_client_cert.pem"), testdata.Path("crl/provider_client_cert.key")); err != nil { return err } if cs.ServerCert1, err = tls.LoadX509KeyPair(testdata.Path("server_cert_1.pem"), testdata.Path("server_key_1.pem")); err != nil { @@ -98,7 +98,7 @@ func (cs *CertStore) LoadCerts() error { if cs.ServerCert2, err = tls.LoadX509KeyPair(testdata.Path("server_cert_2.pem"), testdata.Path("server_key_2.pem")); err != nil { return err } - if cs.ServerCert3, err = tls.LoadX509KeyPair(testdata.Path("crl/provider/server_cert.pem"), testdata.Path("crl/provider/server_cert.key")); err != nil { + if cs.ServerCert3, err = tls.LoadX509KeyPair(testdata.Path("crl/provider_server_cert.pem"), testdata.Path("crl/provider_server_cert.key")); err != nil { return err } if cs.ServerPeer3, err = tls.LoadX509KeyPair(testdata.Path("server_cert_3.pem"), testdata.Path("server_key_3.pem")); err != nil { @@ -113,7 +113,7 @@ func (cs *CertStore) LoadCerts() error { if cs.ClientTrust2, err = readTrustCert(testdata.Path("client_trust_cert_2.pem")); err != nil { return err } - if cs.ClientTrust3, err = readTrustCert(testdata.Path("crl/provider/client_trust_cert.pem")); err != nil { + if cs.ClientTrust3, err = readTrustCert(testdata.Path("crl/provider_client_trust_cert.pem")); err != nil { return err } if cs.ServerTrust1, err = readTrustCert(testdata.Path("server_trust_cert_1.pem")); err != nil { @@ -122,7 +122,7 @@ func (cs *CertStore) LoadCerts() error { if cs.ServerTrust2, err = readTrustCert(testdata.Path("server_trust_cert_2.pem")); err != nil { return err } - if cs.ServerTrust3, err = readTrustCert(testdata.Path("crl/provider/server_trust_cert.pem")); err != nil { + if cs.ServerTrust3, err = readTrustCert(testdata.Path("crl/provider_server_trust_cert.pem")); err != nil { return err } return nil diff --git a/security/advancedtls/testdata/crl/README.md b/security/advancedtls/testdata/crl/README.md index 00cb09c31928..33d5309f567e 100644 --- a/security/advancedtls/testdata/crl/README.md +++ b/security/advancedtls/testdata/crl/README.md @@ -46,3 +46,74 @@ Certificate chain where the leaf is revoked * Subject: C=US, ST=California, L=Mountain View, O=Google LLC, OU=Production, OU=campus-sln, CN=node CA (2021-02-02T07:32:57-08:00) * 6.crl + +## Test Data for testing CRL providers functionality + +To generate test data please follow the steps below or run provider_create.sh +script. All the files have `provider_` prefix. + +We need to generate the following artifacts for testing CRL provider: +* server self signed CA cert +* client self signed CA cert +* server cert signed by client CA +* client cert signed by server CA +* empty crl file +* crl file containing information about revoked server cert + +Please find the related commands below. + +* Generate self signed CAs +``` +$ openssl req -x509 -newkey rsa:4096 -keyout provider_server_trust_key.pem -out provider_server_trust_cert.pem -days 365 -subj "/C=US/ST=VA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.ca.com" -nodes +$ openssl req -x509 -newkey rsa:4096 -keyout provider_client_trust_key.pem -out provider_client_trust_cert.pem -days 365 -subj "/C=US/ST=CA/L=SVL/O=Internet Widgits Pty Ltd" -nodes +``` + +* Generate client and server certs signed by CAs +``` +$ openssl req -newkey rsa:4096 -keyout provider_server_cert.key -out provider_new_cert.csr -nodes -subj "/C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com" -sha256 +$ openssl x509 -req -in provider_new_cert.csr -out provider_server_cert.pem -CA provider_client_trust_cert.pem -CAkey provider_client_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile provider_extensions.conf + +$ openssl req -newkey rsa:4096 -keyout provider_client_cert.key -out provider_new_cert.csr -nodes -subj "/C=US/ST=CA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.com" -sha256 +$ openssl x509 -req -in provider_new_cert.csr -out provider_client_cert.pem -CA provider_server_trust_cert.pem -CAkey provider_server_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile provider_extensions.conf +``` + +Here is the content of `provider_extensions.conf` - +``` +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +basicConstraints = CA:FALSE +keyUsage = digitalSignature, keyEncipherment +``` + +* Generate CRLs + For CRL generation we need 2 more files called `index.txt` and `crlnumber.txt`: +``` +$ echo "1000" > provider_crlnumber.txt +$ touch provider_index.txt +``` +Also we need another config `provider_crl.cnf` - +``` +[ ca ] +default_ca = my_ca + +[ my_ca ] +crl = crl.pem +default_md = sha256 +database = provider_index.txt +crlnumber = provider_crlnumber.txt +default_crl_days = 30 +default_crl_hours = 1 +crl_extensions = crl_ext + +[crl_ext] +# Authority Key Identifier extension +authorityKeyIdentifier=keyid:always,issuer:always +``` + +The commands to generate empty CRL file and CRL file containing revoked server +cert are below. +``` +$ openssl ca -gencrl -keyfile provider_client_trust_key.pem -cert provider_client_trust_cert.pem -out provider_crl_empty.pem -config provider_crl.cnf +$ openssl ca -revoke provider_server_cert.pem -keyfile provider_client_trust_key.pem -cert provider_client_trust_cert.pem -config provider_crl.cnf +$ openssl ca -gencrl -keyfile provider_client_trust_key.pem -cert provider_client_trust_cert.pem -out provider_crl_server_revoked.pem -config provider_crl.cnf +``` \ No newline at end of file diff --git a/security/advancedtls/testdata/crl/provider/README.md b/security/advancedtls/testdata/crl/provider/README.md deleted file mode 100644 index ce916297d3f7..000000000000 --- a/security/advancedtls/testdata/crl/provider/README.md +++ /dev/null @@ -1,75 +0,0 @@ -About This Directory -------------- -The directory contains test data (certificates and CRLs) used for testing CRL -providers functionality. - -How to Generate Test Data Using OpenSSL -------------- - -To generate test data please follow the steps below or run create.sh script. - -We need to generate the following artifacts for testing CRL provider: -* server self signed CA cert -* client self signed CA cert -* server cert signed by client CA -* client cert signed by server CA -* empty crl file -* crl file containing information about revoked server cert - -Please find the related commands below. - -* Generate self signed CAs -``` -$ openssl req -x509 -newkey rsa:4096 -keyout server_trust_key.pem -out server_trust_cert.pem -days 365 -subj "/C=US/ST=VA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.ca.com" -nodes -$ openssl req -x509 -newkey rsa:4096 -keyout client_trust_key.pem -out client_trust_cert.pem -days 365 -subj "/C=US/ST=CA/L=SVL/O=Internet Widgits Pty Ltd" -nodes -``` - -* Generate client and server certs signed by CAs -``` -$ openssl req -newkey rsa:4096 -keyout server_cert.key -out new_cert.csr -nodes -subj "/C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com" -sha256 -$ openssl x509 -req -in new_cert.csr -out server_cert.pem -CA client_trust_cert.pem -CAkey client_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile extensions.conf - -$ openssl req -newkey rsa:4096 -keyout client_cert.key -out new_cert.csr -nodes -subj "/C=US/ST=CA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.com" -sha256 -$ openssl x509 -req -in new_cert.csr -out client_cert.pem -CA server_trust_cert.pem -CAkey server_trust_key.pem -CAcreateserial -days 3650 -sha256 -extfile extensions.conf -``` - -Here is the content of `extensions.conf` - -``` -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid,issuer -basicConstraints = CA:FALSE -keyUsage = digitalSignature, keyEncipherment -``` - -* Generate CRLs -For CRL generation we need 2 more files called `index.txt` and `crlnumber.txt`: -``` -$ echo "1000" > crlnumber.txt -$ touch index.txt -``` -Also we need another config `crl.cnf` - -``` -[ ca ] -default_ca = my_ca - -[ my_ca ] -crl = crl.pem -default_md = sha256 -database = index.txt -crlnumber = crlnumber.txt -default_crl_days = 30 -default_crl_hours = 1 -crl_extensions = crl_ext - -[crl_ext] -# Authority Key Identifier extension -authorityKeyIdentifier=keyid:always,issuer:always -``` - -The commands to generate empty CRL file and CRL file containing revoked server -cert are below. -``` -$ openssl ca -gencrl -keyfile client_trust_key.pem -cert client_trust_cert.pem -out crl_empty.pem -config crl.cnf -$ openssl ca -revoke server_cert.pem -keyfile client_trust_key.pem -cert client_trust_cert.pem -config crl.cnf -$ openssl ca -gencrl -keyfile client_trust_key.pem -cert client_trust_cert.pem -out crl_server_revoked.pem -config crl.cnf -``` \ No newline at end of file diff --git a/security/advancedtls/testdata/crl/provider/client_cert.key b/security/advancedtls/testdata/crl/provider/client_cert.key deleted file mode 100644 index 8049c22fdbf3..000000000000 --- a/security/advancedtls/testdata/crl/provider/client_cert.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -wXbbl1Q950WxvkFwHVPEf0AjcNQ+vkE= ------END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider/client_cert.pem b/security/advancedtls/testdata/crl/provider/client_cert.pem deleted file mode 100644 index ea76996a1f84..000000000000 --- a/security/advancedtls/testdata/crl/provider/client_cert.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFmTCCA4GgAwIBAgIUKoNorgc78v8NhX5pT0eX29QTlxQwDQYJKoZIhvcNAQEL -BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5l -dCBXaWRnaXRzIFB0eSBMdGQxGzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTAe -Fw0yMzA5MjYwNDM5MTlaFw0zMzA5MjMwNDM5MTlaMFcxCzAJBgNVBAYTAlVTMQsw -CQYDVQQIDAJDQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRgw -FgYDVQQDDA9mb28uYmFyLmhvby5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -ggIKAoICAQDE6zfvwT6gRaNXc4sNpDqRQR9lHU53fh8cEuYN0tk9iO7aNJhXWOzx -/UTk5ccf9On91pIUQ1Px+crZvv5wHQXTH0ng4jgKe49tNzi23O7IqwPnTOS0v6Zl -pGzJprrMee+YP34SpbZLBwZW9oj1q0vr12pir35B6yTbJGOxRQXaN7302Knjorvj -GeETgIuDiqHv5swRbiOQS1JfquhHTOPJKWq2uK0r9C31euQiT0u0JB7XHcCJZQJ/ -5ByG7I6N+rysU9u7CHgto92cjz7Oe5Fk4LrKj62bmL6EJMsucZGagtSnuXuuPcCm -BDRzNjjvp1/Mym4Fq5GmpOyRiNXXJgdqhR59uKc0Ed9t8Zg3gHT9kDb4/ubHe3ZY -2gPOSO4QJEt+mBWmV9MN9X7nOk+RjKY9TipMQ0+Tc3oZTdUMNohhUwi0GPuEEjbG -rvIIkpYtTKliLBZI8Nb/F3ibJeODA3NzcUDsTBRk7W6xW7qJSS9Bcl/9xVS5+wgC -JHZAuSCRw6yRPrO56CQqeHrKb+3H+oXHwyt/jdYQWL6qHeo6YCg4EV/C2q5R5crd -l4AhjZOm1Y03EbfbRcn4DntjFpkUkGfAMkSczRGH264DIuTLZ1c0zSM/4GoGbGrw -XbiYi89bzgI7rgGQ7xEpigJT0o6RGA3u6SI/5GHFqcHs5Vou8nbUTQIDAQABo1ow -WDAdBgNVHQ4EFgQUyf6qal9fKpoR1+GMfnFCzoK+ClcwHwYDVR0jBBgwFoAUDjmd -nNwr6MWPMhPftdbsTkQbdG8wCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwDQYJKoZI -hvcNAQELBQADggIBAA+ALoE20z6k0rt5tc1V8OvTav5igJxcO9+gc4G0j+SL6RO+ -Z/uv2PTsR8Lyr0TYaqZQRANDA/hEXJK1UPGlFoVtAPTwLtasrSkvPPFS3puTv7Os -sXODpZsUHHouTQQHiMktCQLwI6W+3B/pH/YMx+vFnf+GQZ+JTfJXzv8UPrtVngU2 -kvbIUd6csWjp1n6KniUf44t6s7n+1XXo3Njaifwx6bLMTAcPyqdzg87VhyDCb0nA -+cJUl/sq38Axj6P5fNh22g8HJXFA0yhJXqIOvGUOikc3Hq4Uyd6gEpisP8dnrhu3 -Pucorv3ccLGJV2Vg+NI0fV/94dX9Yv09Sv0B23uCQqtATdLpL6ZBANn+lX15MkJP -E8kPMEvmi3qRP2lqbssgiyhcHUE0XIE9o/fYnwk8wfMtaS0XPSOWzPddzqjiNqDB -1vsHCWLLBDOvz0nFd5L5P/uzP1M72mfSWbndJnTTQXLOJKpeubyOlFDBLqE399wP -p6H/lsz9y/Qp299CiSpzdI0nWO2KHYbikTt4LicKz2VMRSytpAV0V6AHKC2cZmUz -589Gfn/pgtJ/c2ULRiTDbpnhsRtHWE/oWk/dxZHzs+Cd04fH7+3gxVDGYioF9YUH -nlvnvl+OsEdFckYfVq1zaSIHqKNBJUhf3CEtPmiyZ1a9zc+uwLpd4dPsh7v3 ------END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider/client_trust_cert.pem b/security/advancedtls/testdata/crl/provider/client_trust_cert.pem deleted file mode 100644 index 8f33cc3c0081..000000000000 --- a/security/advancedtls/testdata/crl/provider/client_trust_cert.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFdzCCA1+gAwIBAgIUTzuP0DCVAESFS7E3nDyJIKuDPjowDQYJKoZIhvcNAQEL -BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf -BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA5MjYwMzQ5NTla -Fw0yNDA5MjUwMzQ5NTlaMEsxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoG -A1UEBwwDU1ZMMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC6DLiFSsZNQlctTUKjawO47LRt -Gg4fyerF7TP/+E+eWRxElBDoo7mq3va+x+A4FT9Xqoe20+ZSpXYCEhcATw1JEuXi -j2fPf5p2sHeWnv70/7aMeBhvzfh++qrup5LtwnASqYh6j05wOQFAecdqOlPTT9k9 -39RQmUA6GNoMpv3NjToUM/9MgpvIEKBuiDt/hSUupkTz6P1T1RctKT0AFIA8Z3bJ -n8tbSo0H3R7TGQSIuIzzaFlMrZBOEiwURiX9q8YjVPgw2mZ4d/vDOBvWZxQ04Er8 -Uvkq2eHZdQvfqvkqwwdri+rccI407iCg86XfwaMOt+W+p43SOB+fPa+IB58fvMYt -UtJalpjfEoOSRPrj+c6jRmJC7Ox3hWERHujNfncDf5ezpZNGicjDGyDafOjuButC -4Emq/0Ur/qi9+612wZwBDk3zP0SrhdXVBqfQVKnfm8bLDbV9HmatWTSu3e8zHsIK -RV+4dWZ7D/FtFtrjJGRdwVTN1nQ0XL9jwcXIlb+ADP4aSouHcSo+DJvl4tFYCoQy -icNz3/4UuWvJwSt5wq0OB8Q2yyaYm5+P7dYthBT4VAQNLdPQ4KKy/RyP15EE8Vpq -dECSi6BgiGjeX098qQU/qAzYtTFnbZJeI0u6C5sKDZgRK1RgBo2KZQX11c8P740I -hJhChi/Tv5ciembUfQIDAQABo1MwUTAdBgNVHQ4EFgQUxObRG/aeifFAEvKwMrL2 -7uUMRU4wHwYDVR0jBBgwFoAUxObRG/aeifFAEvKwMrL27uUMRU4wDwYDVR0TAQH/ -BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAjA2Wjwlarht6oGUGw7HY98uAC8gt -w/6m4wRWOkEJkGqW004eNnMqjt7OweJPBviRZafpXjMJE9yy/wGCsSWPpuC7+Akq -j/Xr0npSPS7UlvP0fvSA2a6WM3022tZhj0iWEf32w/m04JthRNNfznYT0KjUDosJ -U36MtesrUTytl6UIW9KSgWQ+jc+rVWwFjL/zCDuoDVsBT/WeHerJghpWp0XHCBxg -3E8ZCex9/u8eWl8YisMLbluV0QuCb7Fa5ybsb0xUShgYiHzj2kaRRB8Dg8ftKhXZ -DLp2b/rGs2F83i9S03DezbTH1zfw/ffjFO/ksxLCsn8xALEt8HbAu9sPNxMPRxgi -y5zIwU4eRjATseArO2cjMWjAwZVu49GjGWtSps1Azx1wUaHh8gbdypamyv8YP/Ie -NTUwZxbwTucMq3TGGOWApeQSFWPA5Sb2w2dBZEZWXX3dp6wVc+EY/C0opiSaUKw9 -S+bpFPgi17pn2wYhtb9Jpo5DJm/iAmH7WrH6hJPfLmg4n3RG1ihsO9LaazQMIknb -DBkjprjnLN+a/YtFukd+XkZ1xuVbTRjYrV7QOJFzidWW5l49xx0ZTvgXbCKj5Wl4 -ZqqU+z8KykBYaC85aBVbVwsbYhRLOilQrbtFNOgQFDnOq4tPjZVXsw/9hgC3FChr -+RQG8YEclj/l45k= ------END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider/client_trust_key.pem b/security/advancedtls/testdata/crl/provider/client_trust_key.pem deleted file mode 100644 index f1aa8ad16119..000000000000 --- a/security/advancedtls/testdata/crl/provider/client_trust_key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -/QygMqmjwwSOJi4P5uS604XabnKDo8V4 ------END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider/create.sh b/security/advancedtls/testdata/crl/provider/create.sh deleted file mode 100755 index 6f1fbbece0a5..000000000000 --- a/security/advancedtls/testdata/crl/provider/create.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash - -# The script contains a sequence of commands described in README.md -openssl req -x509 \ - -newkey rsa:4096 \ - -keyout server_trust_key.pem \ - -out server_trust_cert.pem \ - -days 365 \ - -subj "/C=US/ST=VA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.ca.com" \ - -nodes - -openssl req -x509 \ - -newkey rsa:4096 \ - -keyout client_trust_key.pem \ - -out client_trust_cert.pem \ - -days 365 \ - -subj "/C=US/ST=CA/L=SVL/O=Internet Widgits Pty Ltd" \ - -nodes - -openssl req -newkey rsa:4096 \ - -keyout server_cert.key \ - -out new_cert.csr \ - -nodes \ - -subj "/C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com" \ - -sha256 - -openssl x509 -req \ - -in new_cert.csr \ - -out server_cert.pem \ - -CA client_trust_cert.pem \ - -CAkey client_trust_key.pem \ - -CAcreateserial \ - -days 3650 \ - -sha256 \ - -extfile extensions.conf - -openssl req -newkey rsa:4096 \ - -keyout client_cert.key \ - -out new_cert.csr \ - -nodes \ - -subj "/C=US/ST=CA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.com" \ - -sha256 - -openssl x509 -req \ - -in new_cert.csr \ - -out client_cert.pem \ - -CA server_trust_cert.pem \ - -CAkey server_trust_key.pem \ - -CAcreateserial \ - -days 3650 \ - -sha256 \ - -extfile extensions.conf - -echo "1000" > crlnumber.txt - -touch index.txt - -openssl ca -gencrl \ - -keyfile client_trust_key.pem \ - -cert client_trust_cert.pem \ - -out crl_empty.pem \ - -config crl.cnf - -openssl ca -revoke server_cert.pem \ - -keyfile client_trust_key.pem \ - -cert client_trust_cert.pem \ - -config crl.cnf - -openssl ca -gencrl \ - -keyfile client_trust_key.pem \ - -cert client_trust_cert.pem \ - -out crl_server_revoked.pem \ - -config crl.cnf - - -rm *_csr.pem diff --git a/security/advancedtls/testdata/crl/provider/crl_empty.pem b/security/advancedtls/testdata/crl/provider/crl_empty.pem deleted file mode 100644 index e6742302111f..000000000000 --- a/security/advancedtls/testdata/crl/provider/crl_empty.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN X509 CRL----- -MIIDMTCCARkCAQEwDQYJKoZIhvcNAQELBQAwSzELMAkGA1UEBhMCVVMxCzAJBgNV -BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg -UHR5IEx0ZBcNMjMwOTI2MTU0ODExWhcNMjMxMDI2MTY0ODExWqCBmTCBljCBhgYD -VR0jBH8wfYAUxObRG/aeifFAEvKwMrL27uUMRU6hT6RNMEsxCzAJBgNVBAYTAlVT -MQswCQYDVQQIDAJDQTEMMAoGA1UEBwwDU1ZMMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGSCFE87j9AwlQBEhUuxN5w8iSCrgz46MAsGA1UdFAQEAgIQ -ATANBgkqhkiG9w0BAQsFAAOCAgEAMmcb01RBSBZ1/r7gIceXSB0WftdHgWQ3i+pe -kWdX6kxX/SBR1cd47ygdBSyDaRD2HUzwXAMa4icGFPZ33dtGnkoHjNxUMkK3O+2c -RArJFShXKRatvJgo/4peQx0/2Ho3dCNsrTbcbgQhMgHoRg4Rpgqp6KPFMEN6xysb -5ZJ75Pgp0wHAgcviq7zvoEObqoFX3CPeZ2ZYXO3qQElGFARg3K+2h5bugN7pJqWi -kVjfhf4bjkx2bt/4KDczatjvLk1FF6Mnlx9EWEABijelARI7l002RSrjkOsDa5Ya -vyLIT+6+wcb2qzJ4+ffo+U0Xpy4ZfKDnts2bXs997ro98KDutuJvEOhEcuNqoYxf -3qbzjeNJ82F8LcDHa89suNmqcX1lecEA6tM+kRXsuuQWuNczxDQ+t35jJ8QD0hD4 -QX36JdZsk4Zqffunhm9vhciK2P4YvlAam1bC/fRVUzCnEaeTFXBbpZZfi2+gSJYw -PfLUlvVhICrQVCq2fMEvgaOreuL6prIBKSXDwA/fS1pV54bf9r9a5aBoJ4sYTPkx -4Y0ld8nmRLD5wPPs8ItIRReY2lqz2yR/ocEWbuFN0RqZItPCIsnaujWpF2a0UZeA -rlEW7RpPA8unGy9RasCq42Y4BOkmyF02Dudozvn01mGtWP6UwUyvEhkKIv7yc5ny -KVPnhaE= ------END X509 CRL----- diff --git a/security/advancedtls/testdata/crl/provider/crl_server_revoked.pem b/security/advancedtls/testdata/crl/provider/crl_server_revoked.pem deleted file mode 100644 index c606b13ff2d7..000000000000 --- a/security/advancedtls/testdata/crl/provider/crl_server_revoked.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN X509 CRL----- -MIIDWjCCAUICAQEwDQYJKoZIhvcNAQELBQAwSzELMAkGA1UEBhMCVVMxCzAJBgNV -BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg -UHR5IEx0ZBcNMjMwOTI2MTYwMDI0WhcNMjMxMDI2MTcwMDI0WjAnMCUCFAHXnr3n -I/bocpxh4dhO2W7PGVGMFw0yMzA5MjYxNTU5NTBaoIGZMIGWMIGGBgNVHSMEfzB9 -gBTE5tEb9p6J8UAS8rAysvbu5QxFTqFPpE0wSzELMAkGA1UEBhMCVVMxCzAJBgNV -BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg -UHR5IEx0ZIIUTzuP0DCVAESFS7E3nDyJIKuDPjowCwYDVR0UBAQCAhACMA0GCSqG -SIb3DQEBCwUAA4ICAQA0zQRRPiEy2spIqiidE+10NAiKbbpGCfH7ZcUaxHeBoWTJ -bDU/cNqrOUISqkAR0npWwsTI8KADSv39vXXpush6FfabixeB53Och6S2Y5UGsLVE -6UCqcEOSvh42kIO5PiaLQJrFsFp1c1zWS4AVkPJTcjMTbev2cbPW88wRaRJ/GDKp -nfr0OTWY2PcSjaYjDmcTMO5s8kEQDAoNvhhTA2UUHZKuafcnjsgk3eOu62IXQQbP -fcKIKxSfJgGDgrCxIsKrKoH12gVcbEY7pupL9IdQSihf6ROW+9YphfzrzwL8/V+E -w5nZrd/DTrRSUQl+JcJ74xsjgX4PRCfbXM9FFzVuhD26sKJHwL8NUTwMmydrydF6 -/1k3/K1bYf2+L0y4QN0XNTgfMUoRasPVv4bywg6bpPNXKijVKCyFxWFMrk/6ro5d -pDGYXco3l2g/mbINDT2piZWnBCDxI53wFtVfG99hXH3akSygeEt20cZT+qhNZ9eR -YIibCxWJYQ5xI/hTsoC0YFMirQsoI/CVZEkMOe/L44j5RdTdnemI3yvPanwy2s2l -zFWln9myyy7/OLEkEaYtNfZuCJId00ZHLdMmtpCN+woeiWuyWqext8pmTkMUousb -etIbd596MoTAXKg5AjimRzsaXASxpM6lhjvsBOD2iQ+VI94DQVSVj78lp61x1A== ------END X509 CRL----- diff --git a/security/advancedtls/testdata/crl/provider/crlnumber.txt b/security/advancedtls/testdata/crl/provider/crlnumber.txt deleted file mode 100644 index baccd0398f98..000000000000 --- a/security/advancedtls/testdata/crl/provider/crlnumber.txt +++ /dev/null @@ -1 +0,0 @@ -1003 diff --git a/security/advancedtls/testdata/crl/provider/index.txt b/security/advancedtls/testdata/crl/provider/index.txt deleted file mode 100644 index 62e53ee74054..000000000000 --- a/security/advancedtls/testdata/crl/provider/index.txt +++ /dev/null @@ -1 +0,0 @@ -R 330923042937Z 230926155950Z 01D79EBDE723F6E8729C61E1D84ED96ECF19518C unknown /C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com diff --git a/security/advancedtls/testdata/crl/provider/server_cert.key b/security/advancedtls/testdata/crl/provider/server_cert.key deleted file mode 100644 index fcf5428820c9..000000000000 --- a/security/advancedtls/testdata/crl/provider/server_cert.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -9hhFTf+DrOyZ4lrtz2UjEPiueB2ZbFY= ------END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider/server_cert.pem b/security/advancedtls/testdata/crl/provider/server_cert.pem deleted file mode 100644 index 3fcb91d0d070..000000000000 --- a/security/advancedtls/testdata/crl/provider/server_cert.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFmjCCA4KgAwIBAgIUAdeevecj9uhynGHh2E7Zbs8ZUYwwDQYJKoZIhvcNAQEL -BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf -BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA5MjYwNDI5Mzda -Fw0zMzA5MjMwNDI5MzdaMGcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAG -A1UEBwwJRFVNTVlDSVRZMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBM -dGQxFDASBgNVBAMMC2Zvby5iYXIuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEAmJT4PdVQeo2nzAmGelgpjOsiY1IHXqI/y/nx6xtpamQpAdNDlTyv -y7OgAVJoKJL+R/LnADeWI4yti2wIgGexNZOddxZejobS3c7H5q3LdZp6J0G6Ngas -OWU9tlZeu4Tya3GNOBI4eLCWx25iFe7VzfnUWP3U8hPsXo2+BuEfv/1cg4RBDtx5 -4qFZUB/BzNQaam4Y5a3ENO4uUklcKJ3RSUNFz8dY0nM/rnlSUEKbKrE5h+ykV5Ek -uqn7Njtv69plOFjH/v9jl052BrmiuN11pAnG/jXo0dHGWzMhPc9wj50aLExgKIPc -1fVGvxwivKfcMx38cmJpYQJdBfdueiokQoZ8CmBytcQveQGCYj73QeLz3D5HnPk6 -8WGIjw3rhs7DJHUfJPBmRmA6EQe3xsl0k0CtUV/sUWsfCMaxM8kgEJ8YpL7seYOT -XcTBA5ARd1AAiU356OH1X7XP2a4t6WRQrL+9G2imC3DTtuXrbu4VYG2WvqUxVkid -/YXPYmNbd6UDGuBMFlOMAcinG/Io8G/BEHqOxnBZrwHZIZzBC5kedoCQfJw2wfIz -z9AGEtGFwgZLfwSEy6ics3p+QKq+6Z+tU3UVhKzN8veEllYLuq81+qBwA7GyFzcz -aWzcSVwB7jpIxH343jIsvxxpawrof/9d8n+EwIwVwZeMehPHAYC5TX8CAwEAAaNa -MFgwHQYDVR0OBBYEFNfHmciiVzhqkHW8JLbtMgFNSc+1MB8GA1UdIwQYMBaAFMTm -0Rv2nonxQBLysDKy9u7lDEVOMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMA0GCSqG -SIb3DQEBCwUAA4ICAQAeK587sLTY3XQWKvIaJuWORsxAvhaEXHWTUJ+F0SHeeTdZ -Zgv0uDVQBmnwbndLy8/FKXPcJ2tKsUUCtu+/kCHKf89+yH1cRWU3PXfGAqqz61d1 -curWJw2NYRX9N+SmlcsaLtv1ekRnkJFytehKyINy2c1efL+MURI7kk7IuhwBhNNR -p44rN9On5AJ1TN63LfM48U899kaN/85pHEcFuh1QBfVKKXCzZ6JfE88eKHhN8Sd8 -alHoj9dlZboBqov1ehgrTuBgNR+mf9VkR31SPJe3valswpOwC9Smm3pOVSQW+GRb -tDRY4FExVNINCfwsYyPTJOys1AkYxz8J8cG96AYB11POZr4xniMoyPDdWuieDg7l -vImjlA3ZVAtIi1XWiWvMARTeV2hskVMVZdu62T2p1wzysxAa/L3K4jD0v9X+ID8o -0wREtTVpK7bfob5plbxEJMlOsBi2XWILRQNx5UsMAyBA8c3Ey47dTrJOOFHAx7dK -eUkztawJ3qxzwv6doUTtox8KlEZLijxuA302oQCb/JhuH4VweIUvbYvpbQ+1QKGo -Uqu1LcXj77hQmPp8Iqp4ygcXbKOIIIqNPQu4rcrAhgEAm7Qgv3hRDSYqNwA3e+MW -E9U/iK8rxaYZ7piVBruGHVI3+dNve9GgoINmWR/KTZnlEWBiX1yd0LVkoxBrPw== ------END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider/server_trust_cert.pem b/security/advancedtls/testdata/crl/provider/server_trust_cert.pem deleted file mode 100644 index 23b5cba665e1..000000000000 --- a/security/advancedtls/testdata/crl/provider/server_trust_cert.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFlTCCA32gAwIBAgIUD9Cq8x4SnbHZnInvmS1GtsPPtDwwDQYJKoZIhvcNAQEL -BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5l -dCBXaWRnaXRzIFB0eSBMdGQxGzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTAe -Fw0yMzA5MjYwMzI2NTdaFw0yNDA5MjUwMzI2NTdaMFoxCzAJBgNVBAYTAlVTMQsw -CQYDVQQIDAJWQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRsw -GQYDVQQDDBJmb28uYmFyLmhvby5jYS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC -DwAwggIKAoICAQC3ZtNlVOJz6fz7u+f1W851eUC6YviTNMNdOmsPlE2brQCHTO+b -EJ+1hyY/3v4ZRD1JtQ44Edic+7Bqnr0+EGMHaFaBE5R8InIQbKGSc869UpnZgYrI -Js1Q3icLuWOG8/Ll3hwQLe3P+I8WP5eA1xxZzwbRbSPGx28a2P5eXPOAy8MUzA6G -Hz4YoUNND+0x0sCQUXFVj3+NYCn6GGqwRUj1IfVLA6zpiwNdT5OYDwdEgEJKfhai -ZPcemIV08YgbreKPo52fzuWaS8WaaTNCLGHTuRCMMjOPssRdP9EzbIrTYxDQPRbN -uXPexjP8MWYGtDQqZYqafhDm6egk06Ihz/S7K8vXT3NnB7SDo5/afqBvjp8KCyuI -JPaL6c7mfNiD/dIc93uIdsIz8xbt3W4diZQeHPL8mapnPXYETV2T0U7/G+F/iprH -dCJIPVECGA1BAYnQ5mruFhsbzzVT/fELdRYnXHwxqQr689ChSz4O2mPBk7ECMt+4 -bjmx9mDGISM2sSxTfJ7EjEoz2lxB36OJTQt5OCar5Rv6Gw6eLuW1VvWRiI5QTqAw -LL4tM+bba9P4Zf8ZcHyZtHjpKT5JHcSfiGfpdT/p1YFe7dkVw/uTT/3vIDFgeoeL -ZS6FqAOj7UfIV4L94r4f9ZclyDGuxsMgW3io45H77FVwQ86Q6EMKA/PCawIDAQAB -o1MwUTAdBgNVHQ4EFgQUDjmdnNwr6MWPMhPftdbsTkQbdG8wHwYDVR0jBBgwFoAU -DjmdnNwr6MWPMhPftdbsTkQbdG8wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B -AQsFAAOCAgEASz+WIWtN1piSwUpQ5xmL67q8MK11fEYR4JeeCSbNIz3E/6ExXwLh -m+k/sKuAR0Ab2YRry1gi2AiFLo4rsmkdSNegcndNI4OPiixJyEn1ZwJOaXziAqMH -E1yuwpWQmVyiHurVyILvcD4z/FD6HpVb3AEd09wlnUeLTj0GA3F3k8Q5wb7L33TQ -gD3rfJiR9kTHVCoaZdJlaEZH6WPs5xSLY+tYK8X3+jrglqTB5IDO2I2rr0ySoGDM -I6VS4ZkCpptfDw164Nd8EmL+T3iPFMgJYAUVKvtV3+YnmNz6qaHQDYRTXgdZNA0F -LVyYaxE4UIpm9I9eG1fZat2hOVZUuz8Nr9maeg33IrPOxji9Q9hjL5lG0l+Odqjj -17uQpBew+Ql3EXZ3Nk8RgDvHnl0vWPk0k/KoPbSGQdUFAtF/Rsv8CMEzK4wmtLhX -J0d5uGIX1X5R/mhAm6jl9crnw7XyLbd/3NfoqhKeFN+hEZ0TL9CUTth+O3y2Vj+A -+QNMBopGunnEsKbwfwjUIjqt0z2oH16pn/KFPxF8U+t7x2oYMDNk/8M6xVFkH8v9 -gvuoTt+SoES6+qeU8nAD475SDp0JIaBmaugTkIa7RwsgHNGN/SVzfXcT6J2ht8Wj -o3SbSVt2T1KTGoaOpTv3FRzcwpWnqrOu7ZHxTco37NMTtKqQX6FaEFI= ------END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider/server_trust_key.pem b/security/advancedtls/testdata/crl/provider/server_trust_key.pem deleted file mode 100644 index 635c186fe298..000000000000 --- a/security/advancedtls/testdata/crl/provider/server_trust_key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -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 -rUkotyfd34kJTx26P3/2MF5JtQqJVSCW ------END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider_client_cert.key b/security/advancedtls/testdata/crl/provider_client_cert.key new file mode 100644 index 000000000000..b8b30bf5519c --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_client_cert.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +zDK1RAz8JZHyOJ4tVxpw76AxPUOv+A== +-----END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider_client_cert.pem b/security/advancedtls/testdata/crl/provider_client_cert.pem new file mode 100644 index 000000000000..50acd5a533b3 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_client_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFmTCCA4GgAwIBAgIUH+FcZgWO0XDKIU8T/mcyUE9YFhkwDQYJKoZIhvcNAQEL +BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQxGzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTAe +Fw0yMzEwMjAxODM1NTdaFw0zMzEwMTcxODM1NTdaMFcxCzAJBgNVBAYTAlVTMQsw +CQYDVQQIDAJDQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRgw +FgYDVQQDDA9mb28uYmFyLmhvby5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw +ggIKAoICAQDo9wlMibqqY/mTBdAquY+JORumIunnTXQMrMriB2/afgOJtoo1UABE +2evNabeh+qBlzVe7ouFBLG7fq0MtmiUP38kZRTJQhoXqI/boYgRbGAz5cE23OfJZ +9cvJMAAiVdLXNvmcmWf+CxPN0bKZNgZ0HYpqkalLO5hLUvCc225Kie/0CzuWcrA8 +GIxiMO/3VJj7vdWuiDbRPPGVBfEZEC7jCXUcLVyx17yvnxeODYjHwETVfVegSnHA +P4RY+H0HGwqu0ZBAWnbO9HC3q7o9CdW68KJYFP43c01AHO6vK1Kkkq3MkpP/uVx9 +DGOc+FPovUuui8s8pLQ6SIee2zq12YmQFdDFkwV/iFd6drCNBFWZF2EPRqldfebw +qur8eMAkP7KlnhFQ7C1R5xseW09rsBU4PVtGAuUfE0ATLPIMebiAlPU/1yr4oXFv +PT2qjS6dtgyoYifJSeN6/W2cpVhzmZs0WfJabHBOz+W59oq8Tl19xRb6vekwpbEl +mt2MVXSN1TvweQQPEqh/SjVr8QIp1nwZ1kZAiYAfLvBF9kqpjodg/ZIFpQiL9KmW +wv30IZX4Xv5LvLA/cgjPcGUDxvpyV6VCIbmS/dmqZP/JDE27GKRCG+tsugpaKsZW +8zOJbrBgYVNUM0hU02pXiqMMHGFP/FdYQlxXsiq5FYSxYEtuRNNNgQIDAQABo1ow +WDAdBgNVHQ4EFgQUQd4QRGICOG9KgbDjJTtEXfcdS2swHwYDVR0jBBgwFoAU0UZz +FCfHiQfVrExiD2QPevGA5VIwCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwDQYJKoZI +hvcNAQELBQADggIBAGqa4kbO3mnjuJy9PXvMCvF1BwFhv3ytRhrGU+h9HVbw9l1i +dZMTvS2NRVj4hyqCvtgxOrXKdbGBxcZEajzSJa+rSDmET9PGd7DdBlCLp0VXYgr0 +dQmtFPgwLCuTRYWqxixPMy1Hc2KWvljZ5K9fk8Rz+IL9Y3oqaClMz3yc7F5Ve2fd +dANKaIDdSVo9ScATMfineggfbz6L81dFamSzIBbvwfUX7Puop+Zq/g6sVz1vLg44 +FPK8Etw5LaLC+C7CX7+YD7bCs/v6p/Uv2N6AjP7W6h6zu3HlptytMNRjpcl30ur2 +j10AV4UuhlhZiwysvgJCHahcIaM+jVRXoWfDqnvrJjN1Pe4I7LkE4bNsumWl76An +V4/nRxXWQsXFhqOK3f/prKJzUJLr1Sfg4JafKFulb4HYqvNY16IQhqtSse+MyT0r +KwA+wqBREGosldOb8T9utgBxCmudPPXPlJjcER9WE1qzm5uHyeaNnewTZkF4xiCH +grrW3+ZVcjlLjqj5otGAnr1lMUA7K8bV1jJzB3o+QNCDL1y5uKeOGmLjP8xpQJQW +nPUQEmFa6YPWwWphE6LR5CURARQy9aRPILtPMommEc8KYXlBvYcRX1rGBVXjZNIE +Ibnt/7aJJ8BqwRBylhho1w7O14MZWDB6iR9xNW+4/pgaNYysS85WoF6z56UE +-----END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider_client_trust_cert.pem b/security/advancedtls/testdata/crl/provider_client_trust_cert.pem new file mode 100644 index 000000000000..6c3f76dfa6b6 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_client_trust_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFdzCCA1+gAwIBAgIUKqY2Cg+WHLt9amXOOJBNlBXjEHkwDQYJKoZIhvcNAQEL +BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf +BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzEwMjAxODM1NTRa +Fw0yNDEwMTkxODM1NTRaMEsxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoG +A1UEBwwDU1ZMMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCt+gVYNPbMAsqrhziGDUFmM1aa +rxcKHlfu8DaKYYvs+KTguRRU69IozsdrXR6jzwCGiId926PuJ0FC0fuW52LC/2Xi +FW86MLwgJ0lP+C3WL5D4B6vLCVIaI2YWzLk+mfZ1PclsuvI2Ffq3UWXnZb9o7HIN +OgZ8TMISJGYDCtYHasNiWlYrQecrSf6KjRprT4+USXMDrUP3g0AEq9TYuxLXFZq3 +CMxC+6sV3KWOsNKbAVqQ8xZ/iTgLSzYfifi3ljXliIqj+9vz5Xtb4fxzzwEdqNPJ +tzbPCE+8wOdVg55Lagomb/EYO3wqS4eOzbZ5odX6IMEmsOEOk1+35V6IZtlPSIP2 +zk/JwtC4oZV8XexSu0aw+SSjKPosTQv5VGo1ptJhBxI7AVGWmaquX0C4JIsTIXn3 +HLqFVjdyYSX/fx2yfodmw2xIGfoCbEE3NQAjD/k4zZWhXeqA8qR3kcgwMsI+zsug +LHVC8hbRY5YdQFCH3mBwBsj5PLkcKevRRWSoIHd3m79nTFJClYtr3w6ublwFIndi +lsQysXHG5C49zMDOBJkISQ95dcIJwoQm22ePxjbhs2XCr/zFrtMtmkaD2uQBAt9a +uShY0aXiifkdoZ7fSjbQ7u+DzeaJ64g2RHSCyRbLDtp9L0eolN3q7u00Uc/vSzHW +8DeLGVtdqkmQIh04PQIDAQABo1MwUTAdBgNVHQ4EFgQULiFveRb8zO102dF5iXGd +xqir/4IwHwYDVR0jBBgwFoAULiFveRb8zO102dF5iXGdxqir/4IwDwYDVR0TAQH/ +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAQXb+u9cl2aVtchhW/qhwPjOU82BP +r1hByxiG95pQFdtquf953/jZ4S0GoDFE1lrfqcoaxSKWM6tTIUQxJmJxVDJaA1JI +eyc4qXceEZ/GZYAlP1BqGTsxqWNYVA1suKYMlpz2GZDGY1/M7Ggy56P1V5YeAgce +IGM65aj6eyc5SDtNqWperkpJdr960CkzoHreSbKzcWny7yF5W5L5WeZrovQj+FKI +tdcsajxRbxcTl/zqAVifRPIazfU+0g9pV5WvqF8p8HKFn3LgWZFkR4LfPXzepVy9 +/67R76vp48lmbUdMJ1llMHYHSvbjHwN7iT/MV7R1KUzpmjAJhcBg1GwrwT+hjcMW +bBfJYeAKXbekPWNUC0dpiAiaHR5znAjJ8zYtH9loFrZl5xlSIp8M8EFZiC9uecqL +REWrbjE6vZribAk5x6L2Qabr/7PBmhRPRMulC/Oc8TvBMa2YxjBZowEcXnYIIP0i +aLZ69Lrvle6axQT7ZYynSwrunRAlhVrDP8rAQH1UT1l3OV8GsZ0I3Mht0DEY/s83 +/siCVm3vteMUkv7WA7aKTB63MFjWg8lajbOlmgqLExoHRCAnBNY/NsHkFpm9xt0F +vIrQtU3GXk5Uyx/Vh0se81+0u74UQqZYJ/PbOLTDzRXolGq+zp++eiUMHvYnCXU7 +8vBcs8+CWNGSL20= +-----END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider_client_trust_key.pem b/security/advancedtls/testdata/crl/provider_client_trust_key.pem new file mode 100644 index 000000000000..3d16b50a1f60 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_client_trust_key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +V5VXFg6RJMUHFm56NBP/NTCWFkBD2Dk= +-----END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider_create.sh b/security/advancedtls/testdata/crl/provider_create.sh new file mode 100755 index 000000000000..85189020dc9a --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_create.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# The script contains a sequence of commands described in README.md +openssl req -x509 \ + -newkey rsa:4096 \ + -keyout provider_server_trust_key.pem \ + -out provider_server_trust_cert.pem \ + -days 365 \ + -subj "/C=US/ST=VA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.ca.com" \ + -nodes + +openssl req -x509 \ + -newkey rsa:4096 \ + -keyout provider_client_trust_key.pem \ + -out provider_client_trust_cert.pem \ + -days 365 \ + -subj "/C=US/ST=CA/L=SVL/O=Internet Widgits Pty Ltd" \ + -nodes + +openssl req -newkey rsa:4096 \ + -keyout provider_server_cert.key \ + -out provider_new_cert.csr \ + -nodes \ + -subj "/C=US/ST=CA/L=DUMMYCITY/O=Internet Widgits Pty Ltd/CN=foo.bar.com" \ + -sha256 + +openssl x509 -req \ + -in provider_new_cert.csr \ + -out provider_server_cert.pem \ + -CA provider_client_trust_cert.pem \ + -CAkey provider_client_trust_key.pem \ + -CAcreateserial \ + -days 3650 \ + -sha256 \ + -extfile provider_extensions.conf + +openssl req -newkey rsa:4096 \ + -keyout provider_client_cert.key \ + -out provider_new_cert.csr \ + -nodes \ + -subj "/C=US/ST=CA/O=Internet Widgits Pty Ltd/CN=foo.bar.hoo.com" \ + -sha256 + +openssl x509 -req \ + -in provider_new_cert.csr \ + -out provider_client_cert.pem \ + -CA provider_server_trust_cert.pem \ + -CAkey provider_server_trust_key.pem \ + -CAcreateserial \ + -days 3650 \ + -sha256 \ + -extfile provider_extensions.conf + +echo "1000" > provider_crlnumber.txt + +touch provider_index.txt + +openssl ca -gencrl \ + -keyfile provider_client_trust_key.pem \ + -cert provider_client_trust_cert.pem \ + -out provider_crl_empty.pem \ + -config provider_crl.cnf + +openssl ca -revoke provider_server_cert.pem \ + -keyfile provider_client_trust_key.pem \ + -cert provider_client_trust_cert.pem \ + -config provider_crl.cnf + +openssl ca -gencrl \ + -keyfile provider_client_trust_key.pem \ + -cert provider_client_trust_cert.pem \ + -out provider_crl_server_revoked.pem \ + -config provider_crl.cnf + + +rm *.csr diff --git a/security/advancedtls/testdata/crl/provider/crl.cnf b/security/advancedtls/testdata/crl/provider_crl.cnf similarity index 77% rename from security/advancedtls/testdata/crl/provider/crl.cnf rename to security/advancedtls/testdata/crl/provider_crl.cnf index 55d7e73c7d51..3f12ddfc2edb 100644 --- a/security/advancedtls/testdata/crl/provider/crl.cnf +++ b/security/advancedtls/testdata/crl/provider_crl.cnf @@ -2,10 +2,9 @@ default_ca = my_ca [ my_ca ] -crl = crl.pem default_md = sha256 -database = index.txt -crlnumber = crlnumber.txt +database = provider_index.txt +crlnumber = provider_crlnumber.txt default_crl_days = 30 default_crl_hours = 1 crl_extensions = crl_ext diff --git a/security/advancedtls/testdata/crl/provider_crl_empty.pem b/security/advancedtls/testdata/crl/provider_crl_empty.pem new file mode 100644 index 000000000000..f9e7daae9b65 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_crl_empty.pem @@ -0,0 +1,20 @@ +-----BEGIN X509 CRL----- +MIIDWjCCAUICAQEwDQYJKoZIhvcNAQELBQAwSzELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZBcNMjMxMDIwMTgzNTU3WhcNMjMxMTE5MTkzNTU3WjAnMCUCFCBOb4be +aSBeU1CCmUkUOpLDLh4hFw0yMzEwMjAxODM0MTJaoIGZMIGWMIGGBgNVHSMEfzB9 +gBQuIW95FvzM7XTZ0XmJcZ3GqKv/gqFPpE0wSzELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZIIUKqY2Cg+WHLt9amXOOJBNlBXjEHkwCwYDVR0UBAQCAhAAMA0GCSqG +SIb3DQEBCwUAA4ICAQCpo3SBHdRE+yCNw9dKcvIbDM01jPAYObxggP6YIuiS+ZgN +ozdDsoDBfzpo5gqR7tB4/PbD0TviJ25dWfWr9Kq2aobDrmDaemZ0tnURxiT+mI7e +327P9S1mrjSy07hlM0gX1CA9PmzrhCNiyS7w5EpHStTu768/ftMeostWJvRPBjIO +lVVEgibhxqxj7eWJ0xCMvwmp5oI3OXsjlkv2AvGvwrJI6EPbUKB8Ppa6LAEWaIfL +h0Uvd2U+FJCPdfHtQTTBarGdplS6cRxeR1EFfquHB78zoGE5ZH8sMUOclVAi3vJ7 +81PKce+dUIFhePTh4IKH+OcuMydSaVs6O9Ju8/DNTNMQvUkFqA/9doCDql3aaN+9 +bGjYEJLw5xWOfP8yF5qfppVahHWK4q9Ezm+vmALjcNMt/EcP6Gp7LhHUwnbE18+Y +krwJNG/RNnwDP0eNgeUQA+dGacEFnWX4RQ5lUIm1/DO1IBRHy5vali3qg/0Ryx/R +orafpcqLo6FUjZQz+W4URACzq37oRY4qMBr3yNzVpqaVpej8zluqEOZpf8gpo9EC +Cika7WeHaUk5U1FHAfzccIao+Xh/13nPQsEdR7VjsluNZHr/pOWZEigG0sBbIFxH +qfIZ4QaSnG0cr5XTkEYPu9W8SXD64Y9C1dyYtFFgsDyaBSSwOFvQfnbzZTOGEg== +-----END X509 CRL----- diff --git a/security/advancedtls/testdata/crl/provider_crl_server_revoked.pem b/security/advancedtls/testdata/crl/provider_crl_server_revoked.pem new file mode 100644 index 000000000000..c2ac42642d61 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_crl_server_revoked.pem @@ -0,0 +1,21 @@ +-----BEGIN X509 CRL----- +MIIDgTCCAWkCAQEwDQYJKoZIhvcNAQELBQAwSzELMAkGA1UEBhMCVVMxCzAJBgNV +BAgMAkNBMQwwCgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMg +UHR5IEx0ZBcNMjMxMDIwMTgzNTU3WhcNMjMxMTE5MTkzNTU3WjBOMCUCFCBOb4be +aSBeU1CCmUkUOpLDLh4hFw0yMzEwMjAxODM0MTJaMCUCFCBOb4beaSBeU1CCmUkU +OpLDLh4iFw0yMzEwMjAxODM1NTdaoIGZMIGWMIGGBgNVHSMEfzB9gBQuIW95FvzM +7XTZ0XmJcZ3GqKv/gqFPpE0wSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQww +CgYDVQQHDANTVkwxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIU +KqY2Cg+WHLt9amXOOJBNlBXjEHkwCwYDVR0UBAQCAhABMA0GCSqGSIb3DQEBCwUA +A4ICAQAylv3bjfdzUmWp8vVkIutABIwR4vrO54gVgslg0OUPC1C0iq/knpMywzT0 +xej4IRULVAhkNFE1mOjan37ghqPOvn+WGUlQD1VzCCnCc5aWoLSHNvIpfNe4Z0tU +VUrFMIiRBEAy3dznoTsBjwiXVjHx4MJ7w0cLDBfEsQgpmmdurFkpSSQFkfqG8+BU +Hqde1vdFld7iCwA0ooYTrb/BFUq2/JSz5pgxPZib/oYAxgP452cD6EBeteCMDx6L +WeKYTDqEzrB5fjYJRksNN+FW7nM7saHC4fSwXeAoR5N6uMF+KYMZxhz84G2ljz9T +Wt2e1fJ0hAOu3cxsMmkKRZjpNPmSUMxerylbkL7VXWgms5sohZ62L4GElnxSqVNC +a5Trtqpe6b5UrqIyr6MGzrrVafU/3C+gv5agCO+TuU+9kMv3/EzPn36fTMhtn8NF +PjgoA3DVqkOdTi3FaGBgAzixzeKX5RBkhyB9vlx5ojm3cKyk+CBoBhogadr8FClZ +/0ss98j0cIbWb93LS6et69LvWSoDNLMYKz8zZLqws6o6cIdPITOJbUjss8b3R7i2 +HfdElF7PJMJlW6L+1I8phhiQzCezCoBOdVD6YvdNmyxJj3m29bO3XrOdQf0Qj1QN +vYqyl+js5yNp6pEtwAxHfXqB6VZgmdG22kYHQFRaDdlU7VrzpA== +-----END X509 CRL----- diff --git a/security/advancedtls/testdata/crl/provider/extensions.conf b/security/advancedtls/testdata/crl/provider_extensions.conf similarity index 100% rename from security/advancedtls/testdata/crl/provider/extensions.conf rename to security/advancedtls/testdata/crl/provider_extensions.conf diff --git a/security/advancedtls/testdata/crl/provider_server_cert.key b/security/advancedtls/testdata/crl/provider_server_cert.key new file mode 100644 index 000000000000..52b559ee4f9c --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_server_cert.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQCqf7Se7crI32+K +jcpNPPwTwCuYHRKtdNlZIgGY+Vs2EN6RTC1DEtTTm6QGcHdxh1vCMNv9f7mKQvHo +5jl3KfTY3qpbtgsIrJyCLl+GxBLpFfznU/DytcG5Ahphw9xYCMGa2lEbEM1C4G67 +/FmmkSLOJRPsCehsInLRC+4wgLUTbK8vTGBPzjNPMFlqntRfAxwcIiFjqdYYDBUR +krgT7IjfG5JRQv7QCJfx7bGD0fktBCDA/wuXHZJqoiGWGXoEoCUYky313wyiQHV0 +NqKhzEH9v1VOowTXU7ECPltlhcLvV1xXcCbRcQaswDzVhR+cofQU43Ho1BbQtHZi +zEhYANEC0R+UKl7sPd/efSmMHOQNoUG3QqMQYJKuvrZu7yXMKPFcQuvbIIwBarwK +cuXGZc231v39JXbjapawD9xkmxfOBLMIRdXGndpUiX2egp2QgTYqA7glxCjq+KiY +bNwnUs947gGVVZNq8W7q8BxaNuAllik9hyhQoICO6yjWt2oxoS68vvPZrlJr5sW9 +A5VI2FuS5IUc5G1Y8S6B8ewnJibgLcfKX3gxoUkkQZ9EsYNYaLVONT35J0aghUFO +rd0eGivlzwXN6dsWJIAnrxqrh6EurCVsaso0pNu8tKrAQouyvoVUiWWqwwwhOYOb +LTkqXD30BvOi8v+Set/6TZwAKzHvWwIDAQABAoICAACx7uoQ8m6EM/+0GUWHAJ4R +/qYq7tcPLrhQIlfdzbM4OWK36p1R4n/kVrRXWV1N8x/6Xq3iCz8W4RvqcwSF2BjZ +kNyOhEKqSs8LDQT5yqacRNYqlO+1saRP77dDUE7O5lR7xwXduSsodWXFycBwlLGT +cXPZDH4DBpsh5HwvzM0soxWFxwS8RKBHhFh3bPU1iDP4faYFh2O8tN9H96vOdIu4 +SziSldhXmKBPWusR0ZAPQBTuqpJDTW6q2jPdnGOQGuab6ag4Gw8+77D5bLX3z7MO +8x7pcjfF1bxlGwPxxNGAoSs9aqMYRgcxQhjlZzNrw5jMM+bXlSo6T3CSqvQqOK/m +YBXAvbCCSPYYmFbD/mT2LrHZ3YgdocRqe4zfxSQOn7tNNhwTJtGBWsZlziQ4Az+I +QC5xCZsfCq2QILcvOucomvpnNt8x8zjio+8Gfyr47sxL4Al1SpViY7MP45UmBCBM +hp5nPriaTZL+TOHD5tg/pf+QxTcCF72xwdbnEiPE2xi8AbWEMFwza1VLIXijRSLG +X8WdbFhRtst/HnpfboKqwNFawa8fVuQILfwKLEUBmtlahv+sEd+tQp1JTDnX1jdi +jR/HllRWQeleZanmZpaZdkj0Hur+eVfl3PBb18ShtZ79jWZDOHU/+SWQ4280qc1q +SpkwdTFTXszfQECDt1+1AoIBAQDlIUTEQgUZV3qWOusRXRiwHY72rU98zN69T1NE +4vun7gz9HmHkMktKAns5T3VRzcFR7qzI3eC/Vs6WfJeTOCPbIopYvN2eT22Klmhb +Ptl5gAtxvoWzrKISTJ+Bj7/CChNQsdalZeHZgnAWEo4vQbNW9o2//44iY9HGqLuG +9LUBpVY2h9Zc28PIC4805zjfLme8hvq6MwrbqTdSTXYwLzw1kFFjJrYOJ1o853My +iAyVq/lpOoIenQYQOHH5LOioa+eNVIwSQ4dWzeblDDnloG1kkBU0Y10YbjXapUBF ++aRRHOL9zY1flT/CK4bZYAZGKo7P9SM0mcJIJLGKRHdJIyrXAoIBAQC+fkRi+V6f +h92KNVmo1rCtHmBLRhnYi8udJKZ1EP5nWAW0j2Uut/GwqncxZ6k+q+rc2aFHoaRN +230MgsulQTBNhpUO2ZiFIahddapSn5U8oe/ucRTPcojOUup2GWyU0d8APrmMcgiC +6czl+cOc/khAjvLBJC5QraGtAxE2zm8NihzP3L1OtQuqRd9tzrQS8glKSJB9oT1C +SYV5y5Mu7sMtkj0vtHQL2l3Vbk47IcWi8g4CIkF37gnMwCdewfXSynsLtHQa4ZJk +sdAQdAxSKfUiUvxu5ixLGz54HyiuCTem0VXAPb3uq+39HMr9xO2xxqn0tJN+JseD +lD6nHaDeVPMdAoIBAQCAMBmh1vG1WMybacEDSNs8BH1sIk/bGV7v+IY0fuyd6b9Y +iPvpR/35HORFjt+q8XrbVLVT91X6lh0j8fZ3BayBt5RAywENxZAaPcWKbuIKaIl+ +jEGO4OEXbci7GmoEq9Bcj/HvPM2a+6+rmZv0ckRcPbnWFao2MTQ2eUXY3eS6U/6k +qWBTORwSOe1Xgpi9u9+LiNSTAWVsuQHbSLz7fiGoMeJmn0yxJHEGq9I2DglEXx89 +MN+FMwImZv3UkrxjJWM5HXjz6tW3yaAIustVXWh2H2nNkl2OAnKcrWEFBQJZ4thX +d/1E4WH3RpS93kwES2D0lUep8O/Rnr25Bk7aGxOnAoIBAQCG39wHv8xxY79GFhQP +aULasEE5yr6OBhz6fHKnPIsEHNydRVI8y9yCW4/dGSpJx2uZRzXcA+TTg258pzcN +IKTUn092njZRPM16rs8ThQ4jSf0ZdFNptgyLGUYMrF+m1xnvkHnLqQnBt0xuIHOR ++rCplQzoF3f7g5SPbTaI+YzDp2BTBFW9Ho7N1n8lvk7dgyV0xQAZE0rOXkP1QmBJ +wJ/M6lgMKNZpdgkuDtWxJG5MutmURTDZe17Q69R0URx+TQLl/LSgO8ptJUDOBXyb +yD1aOiulUa9W1klav6UL5FbU9C6k2JJcJLtylSpcl0w8rQ60xg4QKeDlltbteBro +kHk5AoIBAQC3MwEVjuhKx4GQp7+wkmTpuPWBZccw6mjfKoJDg0b6ciRFNinUau0J +ejZRmg9F3OGAsRVE+el3O7tEe6Xk22Ukl3sR+8T9X9vdb9UIch2oiDszXh85aqRa +CqyvNVBDs3S+nWWxRxPLt6OgjdsAoAl7j4Jc4ozUNxQzzXhZe4DOJyLw6mRrRf1+ +5kDWgwq/OoTlytutWMbgsuJm4F81OjcwTNZuV/gSs0kSoWpzD0n5sKZ67FoVWLzi +EKvvrKFG2/BzuvCt8WB1WtIF3CHKFTBd3z/tQq7FuD35/K7HTZjCcdp862KLHVHM +43iT2lxMrKbtvNlNluEqPbJ74GNB4T4c +-----END PRIVATE KEY----- diff --git a/security/advancedtls/testdata/crl/provider_server_cert.pem b/security/advancedtls/testdata/crl/provider_server_cert.pem new file mode 100644 index 000000000000..4e93f3de1f06 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_server_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFmjCCA4KgAwIBAgIUIE5vht5pIF5TUIKZSRQ6ksMuHiIwDQYJKoZIhvcNAQEL +BQAwSzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxITAf +BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzEwMjAxODM1NTVa +Fw0zMzEwMTcxODM1NTVaMGcxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAG +A1UEBwwJRFVNTVlDSVRZMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBM +dGQxFDASBgNVBAMMC2Zvby5iYXIuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEAqn+0nu3KyN9vio3KTTz8E8ArmB0SrXTZWSIBmPlbNhDekUwtQxLU +05ukBnB3cYdbwjDb/X+5ikLx6OY5dyn02N6qW7YLCKycgi5fhsQS6RX851Pw8rXB +uQIaYcPcWAjBmtpRGxDNQuBuu/xZppEiziUT7AnobCJy0QvuMIC1E2yvL0xgT84z +TzBZap7UXwMcHCIhY6nWGAwVEZK4E+yI3xuSUUL+0AiX8e2xg9H5LQQgwP8Llx2S +aqIhlhl6BKAlGJMt9d8MokB1dDaiocxB/b9VTqME11OxAj5bZYXC71dcV3Am0XEG +rMA81YUfnKH0FONx6NQW0LR2YsxIWADRAtEflCpe7D3f3n0pjBzkDaFBt0KjEGCS +rr62bu8lzCjxXELr2yCMAWq8CnLlxmXNt9b9/SV242qWsA/cZJsXzgSzCEXVxp3a +VIl9noKdkIE2KgO4JcQo6viomGzcJ1LPeO4BlVWTavFu6vAcWjbgJZYpPYcoUKCA +juso1rdqMaEuvL7z2a5Sa+bFvQOVSNhbkuSFHORtWPEugfHsJyYm4C3Hyl94MaFJ +JEGfRLGDWGi1TjU9+SdGoIVBTq3dHhor5c8FzenbFiSAJ68aq4ehLqwlbGrKNKTb +vLSqwEKLsr6FVIllqsMMITmDmy05Klw99AbzovL/knrf+k2cACsx71sCAwEAAaNa +MFgwHQYDVR0OBBYEFF+qjGuxabV+x/NnfsDP50cFbBIrMB8GA1UdIwQYMBaAFC4h +b3kW/MztdNnReYlxncaoq/+CMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgWgMA0GCSqG +SIb3DQEBCwUAA4ICAQBX4yzbJ4IewiGMTbSs2aH7x4WSRI7mrGBTwXBjdzQBaIn3 +vNMaKbxmt8BC36MVyGy6xoPOZJZiR2t6P6QjfpIiruIIw5cmqjw/7feyxxKNXIzR +BWXm0Nt/btkjgyHqPvtR7DcT+ko7tG94OJzeqW0ZiHaO9egufhEQjmwi5siKSh8Q +Df9vzNu9tgqlLyQiJZbKW9gmXMG33dIn5Dx9QF2ax+7UXuFqYs8Dwq9slmVkMRrX +2b0/7exS2ExOZpDWPZGc4+jr94MbF5O7sPSF/mDTh4nRGXJN/QBbw6maGtdv/5ov +lJ2z8lwdKiGO548xBX5IcyQrC+qgqo9pEewxWhfX7jfZQ121xkJYBqa/RDFo+f6Q +lJExnsqmLW82ZBkPzdiwW9xCdpirmTHwtizUQ8JlkV3jy9PSxRCaNrCrlhUISdNd +OjcGIlnjgz1bTJGJubsDpIkYUrv6w5WSY1RqYK1B8DnNyoPsHMWC5J0MxKpa/w3L +8VK5FoZthP+zJpT6IOZsGGMZ+X9nuqrpbI43lHMBIC30/fVJalrK/AOOfz9VULG7 +9ALv5OiP++If8+9wj4SOLhjpf+U5R+pH/g2oz3/ODGlJDV+ZX4FaxRUWeZwvGQv5 +aIH5v0zgFZIpyaIsdEm/zONqzqcIyLRMjixe7Xr/Q1vKPZXIVEj0DHvvjsl6+Q== +-----END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider_server_trust_cert.pem b/security/advancedtls/testdata/crl/provider_server_trust_cert.pem new file mode 100644 index 000000000000..84b623b60dc4 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_server_trust_cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFlTCCA32gAwIBAgIUBnhgsNLyVwPm+wWr9DCNqjHi/DYwDQYJKoZIhvcNAQEL +BQAwWjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAlZBMSEwHwYDVQQKDBhJbnRlcm5l +dCBXaWRnaXRzIFB0eSBMdGQxGzAZBgNVBAMMEmZvby5iYXIuaG9vLmNhLmNvbTAe +Fw0yMzEwMjAxODM1NTBaFw0yNDEwMTkxODM1NTBaMFoxCzAJBgNVBAYTAlVTMQsw +CQYDVQQIDAJWQTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRsw +GQYDVQQDDBJmb28uYmFyLmhvby5jYS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCsY0i8YoKj4iwbTk7osHmBmOcfOF1NEVlTRj7Nx7PQ9dEowvnt +zE1392SbDPWDc4VbLBlvK2etBpOgUGMMQpaoMUuhVwe0KX+B0mvmOmmscpFqIml2 +5kRP6lF/ngCQD5W/NBPKyWR0FtghW0kBNU5DwPRTQH+h7LjWwegmOWi2DRwAiL08 +IMDA6akaqyJtCcuY6stWTSxm2yaW7ucXjm/FZBbiXoP4+Fa/Pgjq7kPLCCn/KdSg +av8Rt2ErfYnJUVe7KHt08ZD/z8gMD89RB2nJ/L2Xy7mylDC5yqIayPYiNtVbN/Fb +OaHsaBuVAbOOGV41mF0PrWdj7KQd9zZnIgtEfmK2OggeWCk6qo8qKEigxKI9eYxg +OvXOYevk8ozqqYUvwyhKPqpvFQvepD0w739pWowbKxwW+6xJhPd1yVSye2OLF7Fx +rzPlRXV7GuEDNwqrNbXMU6UdN8795iAIE8dr545S77RyKABiMvcvZBEI/j8ucEQI +as2LaKALfj/yYvaCL4y9CoA4239Q5embElamxKAvgT3CAj5+jcYHGx+IkJUv/1zl +n/ju51C/xfRk/iXf6UU2taODEtBKbC0xzJbnMyXFuYeS59IGo443C9148Rn8eRdF +Cg/hHapmlI3YVAjx61o3Kdgf5aDEj+0LHggfI7bIt2Fil/hmz56dX6sSvQIDAQAB +o1MwUTAdBgNVHQ4EFgQU0UZzFCfHiQfVrExiD2QPevGA5VIwHwYDVR0jBBgwFoAU +0UZzFCfHiQfVrExiD2QPevGA5VIwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B +AQsFAAOCAgEAALpRRiFhBU4ZcreZ3dDMThABAmQi8DAaxpTsLTGsGvvyWXtVPvXH +yWUjZEYYYv9IydBLJ4nACKZtLGm/X0+jBl9H9pYWZP35OYCoFnqAMXTdpmXby442 +F7/HWYBCt6z/9k2dN1Jb0ZoaA4uheBKOe+RP3sjv63il83F4hGXMToiTxQMRfaFj +aiQoXvs+06DozMEhB4d14+Bd18qdkJStFWWLUjsJ4TIpzWh4SKprMvePZoLjS/tZ +y7xatqwHsKoNvie0PjAolJpU+qcDlCp32UOkOYr8YDYojsYefQvb7qjUOfeUUocE +VI8mSLayEA8jF0ZIj4hPq4M1TVfUs168dEbsfnHy50H9yCCS+Qq4ubjiXW2M2U2Y +4XWAu0Kh1W/fxW5WVNwrahidmOc5oie4dAYOUcqZe8GnIG2hwBytUkPg9Vv/ImFw +jnJONDV2WekWAJfDSvRuVFi8RkxOyfCf7hhaGML6euwDwxWKxr/XjyZUSQud8l4a +PutvZB65SY4w8VawchPnZ/Hg9kbOtfauzJTyibiHBsiLZnihxrGK9JV8Nv5hg1es +gV9NIhbctR9N+faz631Dg9wZE3AJPEcuh3c2gYqG3UzbXgXk8xZXD/F6hYhTwqvs +W83vT5i+4qpTCnZZ9Zg7L9fo5KZb0DMTsdBUomH6oLJRKPu45l0rFOA= +-----END CERTIFICATE----- diff --git a/security/advancedtls/testdata/crl/provider_server_trust_key.pem b/security/advancedtls/testdata/crl/provider_server_trust_key.pem new file mode 100644 index 000000000000..4f4236cf0b03 --- /dev/null +++ b/security/advancedtls/testdata/crl/provider_server_trust_key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +FhP/nZ3Y5l4y3XdzasT9ityH9SM= +-----END PRIVATE KEY----- From 1e4c5ac79eab3939bdf2cc72a118a184564c2556 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Fri, 20 Oct 2023 20:22:20 +0000 Subject: [PATCH 47/62] Fix for go.mod and go.sum --- security/advancedtls/crl_provider.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index c69f239951ba..a80ec2b45d34 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -39,7 +39,7 @@ const defaultCRLRefreshDuration = 1 * time.Hour // establishment, so implementations of the CRL function need to be fast, and // slow things such as file IO should be done asynchronously. // -// [A69: CRL Enhancements]: https://github.com/grpc/proposal/pull/382 +// [gRFC A69]: https://github.com/grpc/proposal/blob/dddf32d0116376dd0c48adee7b0071a20bc82b5b/A69-crl-enhancements.md type CRLProvider interface { // CRL accepts x509 Cert and returns back related CRL struct. The CRL struct // can be nil, can contain empty or non-empty list of revoked certificates. @@ -83,7 +83,7 @@ func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { // FileWatcherCRLProvider. type FileWatcherOptions struct { CRLDirectory string // Path of the directory containing CRL files - RefreshDuration time.Duration // Time interval between CRLDirectory scans, can't be smaller than 1 second + RefreshDuration time.Duration // Time interval between CRLDirectory scans, can't be smaller than 1 minute CRLReloadingFailedCallback func(err error) // Custom callback executed when a CRL file can’t be processed } @@ -165,7 +165,7 @@ func (p *FileWatcherCRLProvider) Close() { // structs. Users should not call this function in a loop since it's called // periodically (see FileWatcherOptions.RefreshDuration) by run goroutine. // -// [A69: CRL Enhancements]: https://github.com/grpc/proposal/pull/382 +// [gRFC A69]: https://github.com/grpc/proposal/blob/dddf32d0116376dd0c48adee7b0071a20bc82b5b/A69-crl-enhancements.md func (p *FileWatcherCRLProvider) ScanCRLDirectory() { p.scanMutex.Lock() defer p.scanMutex.Unlock() From f654d18a29aed4b141b36b8995db3e713f74ca8d Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 23 Oct 2023 14:31:35 +0000 Subject: [PATCH 48/62] Fix comment typo --- security/advancedtls/crl_provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index a80ec2b45d34..59c524de75f0 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -131,7 +131,7 @@ func (o *FileWatcherOptions) validate() error { // Checks related to RefreshDuration. if o.RefreshDuration < time.Minute { o.RefreshDuration = defaultCRLRefreshDuration - grpclogLogger.Warningf("RefreshDuration must larger then 1 second: provided value %v, default value will be used %v", o.RefreshDuration, defaultCRLRefreshDuration) + grpclogLogger.Warningf("RefreshDuration must be larger than 1 minute: provided value %v, default value %v will be used.", o.RefreshDuration, defaultCRLRefreshDuration) } return nil } From 53d6b051c9281df35a4cbc953de4ec939e8ff64d Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 23 Oct 2023 16:18:53 +0000 Subject: [PATCH 49/62] Fix for gRFC tag --- security/advancedtls/crl_provider.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 59c524de75f0..08d5d3ceeaf0 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -29,7 +29,7 @@ import ( const defaultCRLRefreshDuration = 1 * time.Hour // CRLProvider is the interface to be implemented to enable custom CRL provider -// behavior. +// behavior, as defined in [gRFC A69]. // // The interface defines how gRPC gets CRLs from the provider during handshakes, // but doesn't prescribe a specific way to load and store CRLs. Such @@ -162,8 +162,9 @@ func (p *FileWatcherCRLProvider) Close() { // ScanCRLDirectory starts the process of scanning // FileWatcherOptions.CRLDirectory and updating in-memory storage of CRL -// structs. Users should not call this function in a loop since it's called -// periodically (see FileWatcherOptions.RefreshDuration) by run goroutine. +// structs, as defined in [gRFC A69]. Users should not call this function in a +// loop since it's called periodically (see FileWatcherOptions.RefreshDuration) +// by run goroutine. // // [gRFC A69]: https://github.com/grpc/proposal/blob/dddf32d0116376dd0c48adee7b0071a20bc82b5b/A69-crl-enhancements.md func (p *FileWatcherCRLProvider) ScanCRLDirectory() { From 1f398eb7f6e8e6f27deec7017788d274edcf0f51 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 23 Oct 2023 19:45:55 +0000 Subject: [PATCH 50/62] Add more details to CRL api godoc comments. --- security/advancedtls/crl_provider.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 08d5d3ceeaf0..6513ad14ccf8 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -41,8 +41,15 @@ const defaultCRLRefreshDuration = 1 * time.Hour // // [gRFC A69]: https://github.com/grpc/proposal/blob/dddf32d0116376dd0c48adee7b0071a20bc82b5b/A69-crl-enhancements.md type CRLProvider interface { - // CRL accepts x509 Cert and returns back related CRL struct. The CRL struct - // can be nil, can contain empty or non-empty list of revoked certificates. + // CRL accepts x509 Cert and returns a related CRL struct, which can contain + // either an empty or non-empty list of revoked certificates. If an error is + // thrown or (nil, nil) is returned, it indicates that we can't load any + // authoritative CRL files (which may not necessarily be a problem). It's not + // considered invalid to have no CRLs if there are no revocations for an + // issuer. In such cases, the status of the check CRL operation is marked as + // RevocationUndetermined, as defined in [RFC5280 - Undetermined]. + // + // [RFC5280 - Undetermined]: https://datatracker.ietf.org/doc/html/rfc5280#section-6.3.3 CRL(cert *x509.Certificate) (*CRL, error) } @@ -74,7 +81,7 @@ func (p *StaticCRLProvider) addCRL(crl *CRL) { p.crls[key] = crl } -// CRL returns CRL struct if it was previously loaded by calling AddCRL. +// CRL returns CRL struct if it was passed to NewStaticCRLProvider. func (p *StaticCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { return p.crls[cert.Issuer.ToRDNSequence().String()], nil } From f3dcca13d31ed5b6caa13164a28d195ac935acf8 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 24 Oct 2023 14:30:41 +0000 Subject: [PATCH 51/62] Address PR comments --- security/advancedtls/crl.go | 10 +++++----- security/advancedtls/crl_provider.go | 15 +++++++++------ security/advancedtls/crl_provider_test.go | 13 ++++++++++++- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/security/advancedtls/crl.go b/security/advancedtls/crl.go index 2c7558148890..4eea8395a061 100644 --- a/security/advancedtls/crl.go +++ b/security/advancedtls/crl.go @@ -103,15 +103,15 @@ type CRL struct { func NewCRL(b []byte) (*CRL, error) { crl, err := parseRevocationList(b) if err != nil { - return nil, fmt.Errorf("parseCrl() failed err = %v", err) + return nil, fmt.Errorf("fail to parse CRL: %v", err) } crlExt, err := parseCRLExtensions(crl) if err != nil { - return nil, fmt.Errorf("parseCRLExtensions() failed err = %v", err) + return nil, fmt.Errorf("fail to parse CRL extensions: %v", err) } crlExt.rawIssuer, err = extractCRLIssuer(b) if err != nil { - return nil, fmt.Errorf("extractCRLIssuer() failed err= %v", err) + return nil, fmt.Errorf("fail to extract CRL issuer failed err= %v", err) } return crlExt, nil } @@ -121,11 +121,11 @@ func NewCRL(b []byte) (*CRL, error) { func ReadCRLFile(path string) (*CRL, error) { b, err := os.ReadFile(path) if err != nil { - return nil, fmt.Errorf("readFile(%v) failed err = %v", path, err) + return nil, fmt.Errorf("cannot read file from provided path %q: %v", path, err) } crl, err := NewCRL(b) if err != nil { - return nil, fmt.Errorf("ReadCRLFile(%v) failed err = %v", path, err) + return nil, fmt.Errorf("cannot construct CRL from file %q: %v", path, err) } return crl, nil } diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 6513ad14ccf8..69641850b31e 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -27,6 +27,7 @@ import ( ) const defaultCRLRefreshDuration = 1 * time.Hour +const minCRLRefreshDuration = 1 * time.Minute // CRLProvider is the interface to be implemented to enable custom CRL provider // behavior, as defined in [gRFC A69]. @@ -115,13 +116,11 @@ func NewFileWatcherCRLProvider(o FileWatcherOptions) (*FileWatcherCRLProvider, e if err := o.validate(); err != nil { return nil, err } - done := make(chan struct{}) - stop := make(chan struct{}) provider := &FileWatcherCRLProvider{ crls: make(map[string]*CRL), opts: o, - stop: stop, - done: done, + stop: make(chan struct{}), + done: make(chan struct{}), } go provider.run() return provider, nil @@ -136,9 +135,13 @@ func (o *FileWatcherOptions) validate() error { return fmt.Errorf("advancedtls: CRLDirectory %v is not readable: %v", o.CRLDirectory, err) } // Checks related to RefreshDuration. - if o.RefreshDuration < time.Minute { + if o.RefreshDuration <= 0 { + grpclogLogger.Warningf("RefreshDuration is not set or negative: provided value %v, default value %v will be used.", o.RefreshDuration, defaultCRLRefreshDuration) o.RefreshDuration = defaultCRLRefreshDuration - grpclogLogger.Warningf("RefreshDuration must be larger than 1 minute: provided value %v, default value %v will be used.", o.RefreshDuration, defaultCRLRefreshDuration) + } + if o.RefreshDuration < minCRLRefreshDuration { + grpclogLogger.Warningf("RefreshDuration must be at least 1 minute: provided value %v, minimum value %v will be used.", o.RefreshDuration, minCRLRefreshDuration) + o.RefreshDuration = minCRLRefreshDuration } return nil } diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 44f73ccb868a..dc20d8e9c1a8 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -101,9 +101,20 @@ func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { t.Fatal("Unexpected error:", err) } if defaultProvider.opts.RefreshDuration != defaultCRLRefreshDuration { - t.Fatalf("RefreshDuration is not properly updated by validate() func") + t.Fatalf("RefreshDuration for defaultCRLRefreshDuration case is not properly updated by validate() func") } defaultProvider.Close() + tooFastRefreshProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{ + CRLDirectory: testdata.Path("crl"), + RefreshDuration: 5 * time.Second, + }) + if err != nil { + t.Fatal("Unexpected error:", err) + } + if tooFastRefreshProvider.opts.RefreshDuration != minCRLRefreshDuration { + t.Fatalf("RefreshDuration for minCRLRefreshDuration case is not properly updated by validate() func") + } + tooFastRefreshProvider.Close() customCallback := func(err error) { fmt.Printf("Custom error message: %v", err) From 131e6e7838c1eb811c87e9b477e9fb1b9f8a8425 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 24 Oct 2023 16:39:18 +0000 Subject: [PATCH 52/62] Address PR comments --- security/advancedtls/crl_provider.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 69641850b31e..cd17f4854199 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -40,7 +40,7 @@ const minCRLRefreshDuration = 1 * time.Minute // establishment, so implementations of the CRL function need to be fast, and // slow things such as file IO should be done asynchronously. // -// [gRFC A69]: https://github.com/grpc/proposal/blob/dddf32d0116376dd0c48adee7b0071a20bc82b5b/A69-crl-enhancements.md +// [gRFC A69]: https://github.com/grpc/proposal/pull/382 type CRLProvider interface { // CRL accepts x509 Cert and returns a related CRL struct, which can contain // either an empty or non-empty list of revoked certificates. If an error is @@ -136,7 +136,7 @@ func (o *FileWatcherOptions) validate() error { } // Checks related to RefreshDuration. if o.RefreshDuration <= 0 { - grpclogLogger.Warningf("RefreshDuration is not set or negative: provided value %v, default value %v will be used.", o.RefreshDuration, defaultCRLRefreshDuration) + grpclogLogger.Infof("RefreshDuration is not set or negative: provided value %v, default value %v will be used.", o.RefreshDuration, defaultCRLRefreshDuration) o.RefreshDuration = defaultCRLRefreshDuration } if o.RefreshDuration < minCRLRefreshDuration { @@ -176,7 +176,7 @@ func (p *FileWatcherCRLProvider) Close() { // loop since it's called periodically (see FileWatcherOptions.RefreshDuration) // by run goroutine. // -// [gRFC A69]: https://github.com/grpc/proposal/blob/dddf32d0116376dd0c48adee7b0071a20bc82b5b/A69-crl-enhancements.md +// [gRFC A69]: https://github.com/grpc/proposal/pull/382 func (p *FileWatcherCRLProvider) ScanCRLDirectory() { p.scanMutex.Lock() defer p.scanMutex.Unlock() From bc14ea8f40dc16cd382e96b93173fbf55db759a9 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 24 Oct 2023 19:47:38 +0000 Subject: [PATCH 53/62] Delete crl_deprecated.go and crl_deprecated_test.go --- security/advancedtls/crl_deprecated.go | 521 ------------- security/advancedtls/crl_deprecated_test.go | 775 -------------------- 2 files changed, 1296 deletions(-) delete mode 100644 security/advancedtls/crl_deprecated.go delete mode 100644 security/advancedtls/crl_deprecated_test.go diff --git a/security/advancedtls/crl_deprecated.go b/security/advancedtls/crl_deprecated.go deleted file mode 100644 index cd7b3d1228ad..000000000000 --- a/security/advancedtls/crl_deprecated.go +++ /dev/null @@ -1,521 +0,0 @@ -// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported -//go:build !go1.19 - -/* - * - * Copyright 2021 gRPC authors. - * - * 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 advancedtls - -import ( - "bytes" - "crypto/sha1" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/binary" - "encoding/hex" - "encoding/pem" - "errors" - "fmt" - "os" - "path/filepath" - "strings" - "time" - - "golang.org/x/crypto/cryptobyte" - cbasn1 "golang.org/x/crypto/cryptobyte/asn1" - "google.golang.org/grpc/grpclog" -) - -var grpclogLogger = grpclog.Component("advancedtls") - -// Cache is an interface to cache CRL files. -// The cache implementation must be concurrency safe. -// A fixed size lru cache from golang-lru is recommended. -type Cache interface { - // Add adds a value to the cache. - Add(key, value interface{}) bool - // Get looks up a key's value from the cache. - Get(key interface{}) (value interface{}, ok bool) -} - -// RevocationConfig contains options for CRL lookup. -type RevocationConfig struct { - // RootDir is the directory to search for CRL files. - // Directory format must match OpenSSL X509_LOOKUP_hash_dir(3). - RootDir string - // AllowUndetermined controls if certificate chains with RevocationUndetermined - // revocation status are allowed to complete. - AllowUndetermined bool - // Cache will store CRL files if not nil, otherwise files are reloaded for every lookup. - Cache Cache -} - -// RevocationStatus is the revocation status for a certificate or chain. -type RevocationStatus int - -const ( - // RevocationUndetermined means we couldn't find or verify a CRL for the cert. - RevocationUndetermined RevocationStatus = iota - // RevocationUnrevoked means we found the CRL for the cert and the cert is not revoked. - RevocationUnrevoked - // RevocationRevoked means we found the CRL and the cert is revoked. - RevocationRevoked -) - -func (s RevocationStatus) String() string { - return [...]string{"RevocationUndetermined", "RevocationUnrevoked", "RevocationRevoked"}[s] -} - -// CRL contains a pkix.CertificateList and parsed -// extensions that aren't provided by the golang CRL parser. -type CRL struct { - CertList *pkix.CertificateList - // RFC5280, 5.2.1, all conforming CRLs must have a AKID with the ID method. - AuthorityKeyID []byte - RawIssuer []byte -} - -const tagDirectoryName = 4 - -var ( - // RFC5280, 5.2.4 id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 } - oidDeltaCRLIndicator = asn1.ObjectIdentifier{2, 5, 29, 27} - // RFC5280, 5.2.5 id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } - oidIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28} - // RFC5280, 5.3.3 id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 } - oidCertificateIssuer = asn1.ObjectIdentifier{2, 5, 29, 29} - // RFC5290, 4.2.1.1 id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } - oidAuthorityKeyIdentifier = asn1.ObjectIdentifier{2, 5, 29, 35} -) - -// x509NameHash implements the OpenSSL X509_NAME_hash function for hashed directory lookups. -// -// NOTE: due to the behavior of asn1.Marshal, if the original encoding of the RDN sequence -// contains strings which do not use the ASN.1 PrintableString type, the name will not be -// re-encoded using those types, resulting in a hash which does not match that produced -// by OpenSSL. -func x509NameHash(r pkix.RDNSequence) string { - var canonBytes []byte - // First, canonicalize all the strings. - for _, rdnSet := range r { - for i, rdn := range rdnSet { - value, ok := rdn.Value.(string) - if !ok { - continue - } - // OpenSSL trims all whitespace, does a tolower, and removes extra spaces between words. - // Implemented in x509_name_canon in OpenSSL - canonStr := strings.Join(strings.Fields( - strings.TrimSpace(strings.ToLower(value))), " ") - // Then it changes everything to UTF8 strings - rdnSet[i].Value = asn1.RawValue{Tag: asn1.TagUTF8String, Bytes: []byte(canonStr)} - - } - } - - // Finally, OpenSSL drops the initial sequence tag - // so we marshal all the RDNs separately instead of as a group. - for _, canonRdn := range r { - b, err := asn1.Marshal(canonRdn) - if err != nil { - continue - } - canonBytes = append(canonBytes, b...) - } - - issuerHash := sha1.Sum(canonBytes) - // Openssl takes the first 4 bytes and encodes them as a little endian - // uint32 and then uses the hex to make the file name. - // In C++, this would be: - // (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) | - // ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L) - // ) & 0xffffffffL; - fileHash := binary.LittleEndian.Uint32(issuerHash[0:4]) - return fmt.Sprintf("%08x", fileHash) -} - -// CheckRevocation checks the connection for revoked certificates based on RFC5280. -// This implementation has the following major limitations: -// - Indirect CRL files are not supported. -// - CRL loading is only supported from directories in the X509_LOOKUP_hash_dir format. -// - OnlySomeReasons is not supported. -// - Delta CRL files are not supported. -// - Certificate CRLDistributionPoint must be URLs, but are then ignored and converted into a file path. -// - CRL checks are done after path building, which goes against RFC4158. -func CheckRevocation(conn tls.ConnectionState, cfg RevocationConfig) error { - return CheckChainRevocation(conn.VerifiedChains, cfg) -} - -// CheckChainRevocation checks the verified certificate chain -// for revoked certificates based on RFC5280. -func CheckChainRevocation(verifiedChains [][]*x509.Certificate, cfg RevocationConfig) error { - // Iterate the verified chains looking for one that is RevocationUnrevoked. - // A single RevocationUnrevoked chain is enough to allow the connection, and a single RevocationRevoked - // chain does not mean the connection should fail. - count := make(map[RevocationStatus]int) - for _, chain := range verifiedChains { - switch checkChain(chain, cfg) { - case RevocationUnrevoked: - // If any chain is RevocationUnrevoked then return no error. - return nil - case RevocationRevoked: - // If this chain is revoked, keep looking for another chain. - count[RevocationRevoked]++ - continue - case RevocationUndetermined: - if cfg.AllowUndetermined { - return nil - } - count[RevocationUndetermined]++ - continue - } - } - return fmt.Errorf("no unrevoked chains found: %v", count) -} - -// checkChain will determine and check all certificates in chain against the CRL -// defined in the certificate with the following rules: -// 1. If any certificate is RevocationRevoked, return RevocationRevoked. -// 2. If any certificate is RevocationUndetermined, return RevocationUndetermined. -// 3. If all certificates are RevocationUnrevoked, return RevocationUnrevoked. -func checkChain(chain []*x509.Certificate, cfg RevocationConfig) RevocationStatus { - chainStatus := RevocationUnrevoked - for _, c := range chain { - switch checkCert(c, chain, cfg) { - case RevocationRevoked: - // Easy case, if a cert in the chain is revoked, the chain is revoked. - return RevocationRevoked - case RevocationUndetermined: - // If we couldn't find the revocation status for a cert, the chain is at best RevocationUndetermined - // keep looking to see if we find a cert in the chain that's RevocationRevoked, - // but return RevocationUndetermined at a minimum. - chainStatus = RevocationUndetermined - case RevocationUnrevoked: - // Continue iterating up the cert chain. - continue - } - } - return chainStatus -} - -func cachedCrl(rawIssuer []byte, cache Cache) (*CRL, bool) { - val, ok := cache.Get(hex.EncodeToString(rawIssuer)) - if !ok { - return nil, false - } - crl, ok := val.(*CRL) - if !ok { - return nil, false - } - // If the CRL is expired, force a reload. - if crl.CertList.HasExpired(time.Now()) { - return nil, false - } - return crl, true -} - -// fetchIssuerCRL fetches and verifies the CRL for rawIssuer from disk or cache if configured in cfg. -func fetchIssuerCRL(rawIssuer []byte, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) (*CRL, error) { - if cfg.Cache != nil { - if crl, ok := cachedCrl(rawIssuer, cfg.Cache); ok { - return crl, nil - } - } - - crl, err := fetchCRL(rawIssuer, cfg) - if err != nil { - return nil, fmt.Errorf("fetchCRL() failed: %v", err) - } - - if err := verifyCRL(crl, rawIssuer, crlVerifyCrt); err != nil { - return nil, fmt.Errorf("verifyCRL() failed: %v", err) - } - if cfg.Cache != nil { - cfg.Cache.Add(hex.EncodeToString(rawIssuer), crl) - } - return crl, nil -} - -// checkCert checks a single certificate against the CRL defined in the certificate. -// It will fetch and verify the CRL(s) defined in the root directory specified by cfg. -// If we can't load any authoritative CRL files, the status is RevocationUndetermined. -// c is the certificate to check. -// crlVerifyCrt is the group of possible certificates to verify the crl. -func checkCert(c *x509.Certificate, crlVerifyCrt []*x509.Certificate, cfg RevocationConfig) RevocationStatus { - crl, err := fetchIssuerCRL(c.RawIssuer, crlVerifyCrt, cfg) - if err != nil { - // We couldn't load any CRL files for the certificate, so we don't know if it's RevocationUnrevoked or not. - grpclogLogger.Warningf("getIssuerCRL(%v) err = %v", c.Issuer, err) - return RevocationUndetermined - } - revocation, err := checkCertRevocation(c, crl) - if err != nil { - grpclogLogger.Warningf("checkCertRevocation(CRL %v) failed: %v", crl.CertList.TBSCertList.Issuer, err) - // We couldn't check the CRL file for some reason, so we don't know if it's RevocationUnrevoked or not. - return RevocationUndetermined - } - // Here we've gotten a CRL that loads and verifies. - // We only handle all-reasons CRL files, so this file - // is authoritative for the certificate. - return revocation -} - -func checkCertRevocation(c *x509.Certificate, crl *CRL) (RevocationStatus, error) { - // Per section 5.3.3 we prime the certificate issuer with the CRL issuer. - // Subsequent entries use the previous entry's issuer. - rawEntryIssuer := crl.RawIssuer - - // Loop through all the revoked certificates. - for _, revCert := range crl.CertList.TBSCertList.RevokedCertificates { - // 5.3 Loop through CRL entry extensions for needed information. - for _, ext := range revCert.Extensions { - if oidCertificateIssuer.Equal(ext.Id) { - extIssuer, err := parseCertIssuerExt(ext) - if err != nil { - grpclogLogger.Info(err) - if ext.Critical { - return RevocationUndetermined, err - } - // Since this is a non-critical extension, we can skip it even though - // there was a parsing failure. - continue - } - rawEntryIssuer = extIssuer - } else if ext.Critical { - return RevocationUndetermined, fmt.Errorf("checkCertRevocation: Unhandled critical extension: %v", ext.Id) - } - } - - // If the issuer and serial number appear in the CRL, the certificate is revoked. - if bytes.Equal(c.RawIssuer, rawEntryIssuer) && c.SerialNumber.Cmp(revCert.SerialNumber) == 0 { - // CRL contains the serial, so return revoked. - return RevocationRevoked, nil - } - } - // We did not find the serial in the CRL file that was valid for the cert - // so the certificate is not revoked. - return RevocationUnrevoked, nil -} - -func parseCertIssuerExt(ext pkix.Extension) ([]byte, error) { - // 5.3.3 Certificate Issuer - // CertificateIssuer ::= GeneralNames - // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName - var generalNames []asn1.RawValue - if rest, err := asn1.Unmarshal(ext.Value, &generalNames); err != nil || len(rest) != 0 { - return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) - } - - for _, generalName := range generalNames { - // GeneralName ::= CHOICE { - // otherName [0] OtherName, - // rfc822Name [1] IA5String, - // dNSName [2] IA5String, - // x400Address [3] ORAddress, - // directoryName [4] Name, - // ediPartyName [5] EDIPartyName, - // uniformResourceIdentifier [6] IA5String, - // iPAddress [7] OCTET STRING, - // registeredID [8] OBJECT IDENTIFIER } - if generalName.Tag == tagDirectoryName { - return generalName.Bytes, nil - } - } - // Conforming CRL issuers MUST include in this extension the - // distinguished name (DN) from the issuer field of the certificate that - // corresponds to this CRL entry. - // If we couldn't get a directoryName, we can't reason about this file so cert status is - // RevocationUndetermined. - return nil, errors.New("no DN found in certificate issuer") -} - -// RFC 5280, 4.2.1.1 -type authKeyID struct { - ID []byte `asn1:"optional,tag:0"` -} - -// RFC5280, 5.2.5 -// id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 } - -// IssuingDistributionPoint ::= SEQUENCE { -// distributionPoint [0] DistributionPointName OPTIONAL, -// onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, -// onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, -// onlySomeReasons [3] ReasonFlags OPTIONAL, -// indirectCRL [4] BOOLEAN DEFAULT FALSE, -// onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } - -// -- at most one of onlyContainsUserCerts, onlyContainsCACerts, -// -- and onlyContainsAttributeCerts may be set to TRUE. -type issuingDistributionPoint struct { - DistributionPoint asn1.RawValue `asn1:"optional,tag:0"` - OnlyContainsUserCerts bool `asn1:"optional,tag:1"` - OnlyContainsCACerts bool `asn1:"optional,tag:2"` - OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"` - IndirectCRL bool `asn1:"optional,tag:4"` - OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"` -} - -// parseCRLExtensions parses the extensions for a CRL -// and checks that they're supported by the parser. -func parseCRLExtensions(c *pkix.CertificateList) (*CRL, error) { - if c == nil { - return nil, errors.New("c is nil, expected any value") - } - certList := &CRL{CertList: c} - - for _, ext := range c.TBSCertList.Extensions { - switch { - case oidDeltaCRLIndicator.Equal(ext.Id): - return nil, fmt.Errorf("delta CRLs unsupported") - - case oidAuthorityKeyIdentifier.Equal(ext.Id): - var a authKeyID - if rest, err := asn1.Unmarshal(ext.Value, &a); err != nil { - return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) - } else if len(rest) != 0 { - return nil, errors.New("trailing data after AKID extension") - } - certList.AuthorityKeyID = a.ID - - case oidIssuingDistributionPoint.Equal(ext.Id): - var dp issuingDistributionPoint - if rest, err := asn1.Unmarshal(ext.Value, &dp); err != nil { - return nil, fmt.Errorf("asn1.Unmarshal failed: %v", err) - } else if len(rest) != 0 { - return nil, errors.New("trailing data after IssuingDistributionPoint extension") - } - - if dp.OnlyContainsUserCerts || dp.OnlyContainsCACerts || dp.OnlyContainsAttributeCerts { - return nil, errors.New("CRL only contains some certificate types") - } - if dp.IndirectCRL { - return nil, errors.New("indirect CRLs unsupported") - } - if dp.OnlySomeReasons.BitLength != 0 { - return nil, errors.New("onlySomeReasons unsupported") - } - - case ext.Critical: - return nil, fmt.Errorf("unsupported critical extension: %v", ext.Id) - } - } - - if len(certList.AuthorityKeyID) == 0 { - return nil, errors.New("authority key identifier extension missing") - } - return certList, nil -} - -func fetchCRL(rawIssuer []byte, cfg RevocationConfig) (*CRL, error) { - var parsedCRL *CRL - // 6.3.3 (a) (1) (ii) - // According to X509_LOOKUP_hash_dir the format is issuer_hash.rN where N is an increasing number. - // There are no gaps, so we break when we can't find a file. - for i := 0; ; i++ { - // Unmarshal to RDNSeqence according to http://go/godoc/crypto/x509/pkix/#Name. - var r pkix.RDNSequence - rest, err := asn1.Unmarshal(rawIssuer, &r) - if len(rest) != 0 || err != nil { - return nil, fmt.Errorf("asn1.Unmarshal(Issuer) len(rest) = %d failed: %v", len(rest), err) - } - crlPath := fmt.Sprintf("%s.r%d", filepath.Join(cfg.RootDir, x509NameHash(r)), i) - crlBytes, err := os.ReadFile(crlPath) - if err != nil { - // Break when we can't read a CRL file. - grpclogLogger.Infof("readFile: %v", err) - break - } - - crl, err := x509.ParseCRL(crlBytes) - if err != nil { - // Parsing errors for a CRL shouldn't happen so fail. - return nil, fmt.Errorf("x509.ParseCrl(%v) failed: %v", crlPath, err) - } - var certList *CRL - if certList, err = parseCRLExtensions(crl); err != nil { - grpclogLogger.Infof("fetchCRL: unsupported crl %v: %v", crlPath, err) - // Continue to find a supported CRL - continue - } - - rawCRLIssuer, err := extractCRLIssuer(crlBytes) - if err != nil { - return nil, err - } - certList.RawIssuer = rawCRLIssuer - // RFC5280, 6.3.3 (b) Verify the issuer and scope of the complete CRL. - if bytes.Equal(rawIssuer, rawCRLIssuer) { - parsedCRL = certList - // Continue to find the highest number in the .rN suffix. - continue - } - } - - if parsedCRL == nil { - return nil, fmt.Errorf("fetchCrls no CRLs found for issuer") - } - return parsedCRL, nil -} - -func verifyCRL(crl *CRL, rawIssuer []byte, chain []*x509.Certificate) error { - // RFC5280, 6.3.3 (f) Obtain and validateate the certification path for the issuer of the complete CRL - // We intentionally limit our CRLs to be signed with the same certificate path as the certificate - // so we can use the chain from the connection. - - for _, c := range chain { - // Use the key where the subject and KIDs match. - // This departs from RFC4158, 3.5.12 which states that KIDs - // cannot eliminate certificates, but RFC5280, 5.2.1 states that - // "Conforming CRL issuers MUST use the key identifier method, and MUST - // include this extension in all CRLs issued." - // So, this is much simpler than RFC4158 and should be compatible. - if bytes.Equal(c.SubjectKeyId, crl.AuthorityKeyID) && bytes.Equal(c.RawSubject, crl.RawIssuer) { - // RFC5280, 6.3.3 (g) Validate signature. - return c.CheckCRLSignature(crl.CertList) - } - } - return fmt.Errorf("verifyCRL: No certificates mached CRL issuer (%v)", crl.CertList.TBSCertList.Issuer) -} - -var crlPemPrefix = []byte("-----BEGIN X509 CRL") - -// extractCRLIssuer extracts the raw ASN.1 encoding of the CRL issuer. Due to the design of -// pkix.CertificateList and pkix.RDNSequence, it is not possible to reliably marshal the -// parsed Issuer to it's original raw encoding. -func extractCRLIssuer(crlBytes []byte) ([]byte, error) { - if bytes.HasPrefix(crlBytes, crlPemPrefix) { - block, _ := pem.Decode(crlBytes) - if block != nil && block.Type == "X509 CRL" { - crlBytes = block.Bytes - } - } - - der := cryptobyte.String(crlBytes) - var issuer cryptobyte.String - if !der.ReadASN1(&der, cbasn1.SEQUENCE) || - !der.ReadASN1(&der, cbasn1.SEQUENCE) || - !der.SkipOptionalASN1(cbasn1.INTEGER) || - !der.SkipASN1(cbasn1.SEQUENCE) || - !der.ReadASN1Element(&issuer, cbasn1.SEQUENCE) { - return nil, errors.New("extractCRLIssuer: invalid ASN.1 encoding") - } - return issuer, nil -} diff --git a/security/advancedtls/crl_deprecated_test.go b/security/advancedtls/crl_deprecated_test.go deleted file mode 100644 index e16ec7ef2599..000000000000 --- a/security/advancedtls/crl_deprecated_test.go +++ /dev/null @@ -1,775 +0,0 @@ -// TODO(@gregorycooke) - Remove file when only golang 1.19+ is supported -//go:build !go1.19 - -/* - * - * Copyright 2021 gRPC authors. - * - * 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 advancedtls - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/hex" - "encoding/pem" - "fmt" - "math/big" - "net" - "os" - "path" - "strings" - "testing" - "time" - - lru "github.com/hashicorp/golang-lru" - "google.golang.org/grpc/security/advancedtls/testdata" -) - -func TestX509NameHash(t *testing.T) { - nameTests := []struct { - in pkix.Name - out string - }{ - { - in: pkix.Name{ - Country: []string{"US"}, - Organization: []string{"Example"}, - }, - out: "9cdd41ff", - }, - { - in: pkix.Name{ - Country: []string{"us"}, - Organization: []string{"example"}, - }, - out: "9cdd41ff", - }, - { - in: pkix.Name{ - Country: []string{" us"}, - Organization: []string{"example"}, - }, - out: "9cdd41ff", - }, - { - in: pkix.Name{ - Country: []string{"US"}, - Province: []string{"California"}, - Locality: []string{"Mountain View"}, - Organization: []string{"BoringSSL"}, - }, - out: "c24414d9", - }, - { - in: pkix.Name{ - Country: []string{"US"}, - Province: []string{"California"}, - Locality: []string{"Mountain View"}, - Organization: []string{"BoringSSL"}, - }, - out: "c24414d9", - }, - { - in: pkix.Name{ - SerialNumber: "87f4514475ba0a2b", - }, - out: "9dc713cd", - }, - { - in: pkix.Name{ - Country: []string{"US"}, - Province: []string{"California"}, - Locality: []string{"Mountain View"}, - Organization: []string{"Google LLC"}, - OrganizationalUnit: []string{"Production", "campus-sln"}, - CommonName: "Root CA (2021-02-02T07:30:36-08:00)", - }, - out: "0b35a562", - }, - { - in: pkix.Name{ - ExtraNames: []pkix.AttributeTypeAndValue{ - {Type: asn1.ObjectIdentifier{5, 5, 5, 5}, Value: "aaaa"}, - }, - }, - out: "eea339da", - }, - } - for _, tt := range nameTests { - t.Run(tt.in.String(), func(t *testing.T) { - h := x509NameHash(tt.in.ToRDNSequence()) - if h != tt.out { - t.Errorf("x509NameHash(%v): Got %v wanted %v", tt.in, h, tt.out) - } - }) - } -} - -func TestUnsupportedCRLs(t *testing.T) { - crlBytesSomeReasons := []byte(`-----BEGIN X509 CRL----- -MIIEeDCCA2ACAQEwDQYJKoZIhvcNAQELBQAwQjELMAkGA1UEBhMCVVMxHjAcBgNV -BAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczETMBEGA1UEAxMKR1RTIENBIDFPMRcN -MjEwNDI2MTI1OTQxWhcNMjEwNTA2MTE1OTQwWjCCAn0wIgIRAPOOG3L4VLC7CAAA -AABxQgEXDTIxMDQxOTEyMTgxOFowIQIQUK0UwBZkVdQIAAAAAHFCBRcNMjEwNDE5 -MTIxODE4WjAhAhBRIXBJaKoQkQgAAAAAcULHFw0yMTA0MjAxMjE4MTdaMCICEQCv -qQWUq5UxmQgAAAAAcULMFw0yMTA0MjAxMjE4MTdaMCICEQDdv5k1kKwKTQgAAAAA -cUOQFw0yMTA0MjExMjE4MTZaMCICEQDGIEfR8N9sEAgAAAAAcUOWFw0yMTA0MjEx -MjE4MThaMCECEBHgbLXlj5yUCAAAAABxQ/IXDTIxMDQyMTIzMDAyNlowIQIQE1wT -2GGYqKwIAAAAAHFD7xcNMjEwNDIxMjMwMDI5WjAiAhEAo/bSyDjpVtsIAAAAAHFE -txcNMjEwNDIyMjMwMDI3WjAhAhARdCrSrHE0dAgAAAAAcUS/Fw0yMTA0MjIyMzAw -MjhaMCECEHONohfWn3wwCAAAAABxRX8XDTIxMDQyMzIzMDAyOVowIgIRAOYkiUPA -os4vCAAAAABxRYgXDTIxMDQyMzIzMDAyOFowIQIQRNTow5Eg2gEIAAAAAHFGShcN -MjEwNDI0MjMwMDI2WjAhAhBX32dH4/WQ6AgAAAAAcUZNFw0yMTA0MjQyMzAwMjZa -MCICEQDHnUM1vsaP/wgAAAAAcUcQFw0yMTA0MjUyMzAwMjZaMCECEEm5rvmL8sj6 -CAAAAABxRxQXDTIxMDQyNTIzMDAyN1owIQIQW16OQs4YQYkIAAAAAHFIABcNMjEw -NDI2MTI1NDA4WjAhAhAhSohpYsJtDQgAAAAAcUgEFw0yMTA0MjYxMjU0MDlaoGkw -ZzAfBgNVHSMEGDAWgBSY0fhuEOvPm+xgnxiQG6DrfQn9KzALBgNVHRQEBAICBngw -NwYDVR0cAQH/BC0wK6AmoCSGImh0dHA6Ly9jcmwucGtpLmdvb2cvR1RTMU8xY29y -ZS5jcmyBAf8wDQYJKoZIhvcNAQELBQADggEBADPBXbxVxMJ1HC7btXExRUpJHUlU -YbeCZGx6zj5F8pkopbmpV7cpewwhm848Fx4VaFFppZQZd92O08daEC6aEqoug4qF -z6ZrOLzhuKfpW8E93JjgL91v0FYN7iOcT7+ERKCwVEwEkuxszxs7ggW6OJYJNvHh -priIdmcPoiQ3ZrIRH0vE3BfUcNXnKFGATWuDkiRI0I4A5P7NiOf+lAuGZet3/eom -0chgts6sdau10GfeUpHUd4f8e93cS/QeLeG16z7LC8vRLstU3m3vrknpZbdGqSia -97w66mqcnQh9V0swZiEnVLmLufaiuDZJ+6nUzSvLqBlb/ei3T/tKV0BoKJA= ------END X509 CRL-----`) - - crlBytesIndirect := []byte(`-----BEGIN X509 CRL----- -MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU -ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg -Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 -MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG -EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 -MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 -MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV -BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j -BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ -BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG -SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS -TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG -NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq -XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF -6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 -qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 ------END X509 CRL-----`) - - var tests = []struct { - desc string - in []byte - }{ - { - desc: "some reasons", - in: crlBytesSomeReasons, - }, - { - desc: "indirect", - in: crlBytesIndirect, - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - crl, err := x509.ParseCRL(tt.in) - if err != nil { - t.Fatal(err) - } - if _, err := parseCRLExtensions(crl); err == nil { - t.Error("expected error got ok") - } - }) - } -} - -func TestCheckCertRevocation(t *testing.T) { - dummyCrlFile := []byte(`-----BEGIN X509 CRL----- -MIIDGjCCAgICAQEwDQYJKoZIhvcNAQELBQAwdjELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCkNhbGlmb3JuaWExFDASBgNVBAoTC1Rlc3RpbmcgTHRkMSowKAYDVQQLEyFU -ZXN0aW5nIEx0ZCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEDAOBgNVBAMTB1Rlc3Qg -Q0EXDTIxMDExNjAyMjAxNloXDTIxMDEyMDA2MjAxNlowgfIwbAIBAhcNMjEwMTE2 -MDIyMDE2WjBYMAoGA1UdFQQDCgEEMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQG -EwNVU0ExDTALBgNVBAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0 -MTAgAgEDFw0yMTAxMTYwMjIwMTZaMAwwCgYDVR0VBAMKAQEwYAIBBBcNMjEwMTE2 -MDIyMDE2WjBMMEoGA1UdHQEB/wRAMD6kPDA6MQwwCgYDVQQGEwNVU0ExDTALBgNV -BAcTBGhlcmUxCzAJBgNVBAoTAnVzMQ4wDAYDVQQDEwVUZXN0MqBjMGEwHwYDVR0j -BBgwFoAURJSDWAOfhGCryBjl8dsQjBitl3swCgYDVR0UBAMCAQEwMgYDVR0cAQH/ -BCgwJqAhoB+GHWh0dHA6Ly9jcmxzLnBraS5nb29nL3Rlc3QuY3JshAH/MA0GCSqG -SIb3DQEBCwUAA4IBAQBVXX67mr2wFPmEWCe6mf/wFnPl3xL6zNOl96YJtsd7ulcS -TEbdJpaUnWFQ23+Tpzdj/lI2aQhTg5Lvii3o+D8C5r/Jc5NhSOtVJJDI/IQLh4pG -NgGdljdbJQIT5D2Z71dgbq1ocxn8DefZIJjO3jp8VnAm7AIMX2tLTySzD2MpMeMq -XmcN4lG1e4nx+xjzp7MySYO42NRY3LkphVzJhu3dRBYhBKViRJxw9hLttChitJpF -6Kh6a0QzrEY/QDJGhE1VrAD2c5g/SKnHPDVoCWo4ACIICi76KQQSIWfIdp4W/SY3 -qsSIp8gfxSyzkJP+Ngkm2DdLjlJQCZ9R0MZP9Xj4 ------END X509 CRL-----`) - crl, err := x509.ParseCRL(dummyCrlFile) - if err != nil { - t.Fatalf("x509.ParseCRL(dummyCrlFile) failed: %v", err) - } - crlExt := &CRL{certList: crl} - var crlIssuer pkix.Name - crlIssuer.FillFromRDNSequence(&crl.TBSCertList.Issuer) - - var revocationTests = []struct { - desc string - in x509.Certificate - revoked RevocationStatus - }{ - { - desc: "Single revoked", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test1", - }, - SerialNumber: big.NewInt(2), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationRevoked, - }, - { - desc: "Revoked no entry issuer", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test1", - }, - SerialNumber: big.NewInt(3), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationRevoked, - }, - { - desc: "Revoked new entry issuer", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test2", - }, - SerialNumber: big.NewInt(4), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationRevoked, - }, - { - desc: "Single unrevoked", - in: x509.Certificate{ - Issuer: pkix.Name{ - Country: []string{"USA"}, - Locality: []string{"here"}, - Organization: []string{"us"}, - CommonName: "Test2", - }, - SerialNumber: big.NewInt(1), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationUnrevoked, - }, - { - desc: "Single unrevoked Issuer", - in: x509.Certificate{ - Issuer: crlIssuer, - SerialNumber: big.NewInt(2), - CRLDistributionPoints: []string{"test"}, - }, - revoked: RevocationUnrevoked, - }, - } - - for _, tt := range revocationTests { - rawIssuer, err := asn1.Marshal(tt.in.Issuer.ToRDNSequence()) - if err != nil { - t.Fatalf("asn1.Marshal(%v) failed: %v", tt.in.Issuer.ToRDNSequence(), err) - } - tt.in.RawIssuer = rawIssuer - t.Run(tt.desc, func(t *testing.T) { - rev, err := checkCertRevocation(&tt.in, crlExt) - if err != nil { - t.Errorf("checkCertRevocation(%v) err = %v", tt.in.Issuer, err) - } else if rev != tt.revoked { - t.Errorf("checkCertRevocation(%v(%v)) returned %v wanted %v", - tt.in.Issuer, tt.in.SerialNumber, rev, tt.revoked) - } - }) - } -} - -func makeChain(t *testing.T, name string) []*x509.Certificate { - t.Helper() - - certChain := make([]*x509.Certificate, 0) - - rest, err := os.ReadFile(name) - if err != nil { - t.Fatalf("os.ReadFile(%v) failed %v", name, err) - } - for len(rest) > 0 { - var block *pem.Block - block, rest = pem.Decode(rest) - c, err := x509.ParseCertificate(block.Bytes) - if err != nil { - t.Fatalf("ParseCertificate error %v", err) - } - t.Logf("Parsed Cert sub = %v iss = %v", c.Subject, c.Issuer) - certChain = append(certChain, c) - } - return certChain -} - -func loadCRL(t *testing.T, path string) *CRL { - b, err := os.ReadFile(path) - if err != nil { - t.Fatalf("readFile(%v) failed err = %v", path, err) - } - crl, err := x509.ParseCRL(b) - if err != nil { - t.Fatalf("ParseCrl(%v) failed err = %v", path, err) - } - crlExt, err := parseCRLExtensions(crl) - if err != nil { - t.Fatalf("parseCRLExtensions(%v) failed err = %v", path, err) - } - crlExt.rawIssuer, err = extractCRLIssuer(b) - if err != nil { - t.Fatalf("extractCRLIssuer(%v) failed err= %v", path, err) - } - return crlExt -} - -func TestCachedCRL(t *testing.T) { - cache, err := lru.New(5) - if err != nil { - t.Fatalf("lru.New: err = %v", err) - } - - tests := []struct { - desc string - val interface{} - ok bool - }{ - { - desc: "Valid", - val: &CRL{ - certList: &pkix.CertificateList{ - TBSCertList: pkix.TBSCertificateList{ - NextUpdate: time.Now().Add(time.Hour), - }, - }}, - ok: true, - }, - { - desc: "Expired", - val: &CRL{ - certList: &pkix.CertificateList{ - TBSCertList: pkix.TBSCertificateList{ - NextUpdate: time.Now().Add(-time.Hour), - }, - }}, - ok: false, - }, - { - desc: "Wrong Type", - val: "string", - ok: false, - }, - { - desc: "Empty", - val: nil, - ok: false, - }, - } - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - if tt.val != nil { - cache.Add(hex.EncodeToString([]byte(tt.desc)), tt.val) - } - _, ok := cachedCrl([]byte(tt.desc), cache) - if tt.ok != ok { - t.Errorf("Cache ok error expected %v vs %v", tt.ok, ok) - } - }) - } -} - -func TestGetIssuerCRLCache(t *testing.T) { - cache, err := lru.New(5) - if err != nil { - t.Fatalf("lru.New: err = %v", err) - } - - tests := []struct { - desc string - rawIssuer []byte - certs []*x509.Certificate - }{ - { - desc: "Valid", - rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - }, - { - desc: "Unverified", - rawIssuer: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1].RawIssuer, - }, - { - desc: "Not Found", - rawIssuer: []byte("not_found"), - }, - } - - for _, tt := range tests { - t.Run(tt.desc, func(t *testing.T) { - cache.Purge() - _, err := fetchIssuerCRL(tt.rawIssuer, tt.certs, RevocationConfig{ - RootDir: testdata.Path("."), - Cache: cache, - }) - if err == nil && cache.Len() == 0 { - t.Error("Verified CRL not added to cache") - } - if err != nil && cache.Len() != 0 { - t.Error("Unverified CRL added to cache") - } - }) - } -} - -func TestVerifyCrl(t *testing.T) { - tampered := loadCRL(t, testdata.Path("crl/1.crl")) - // Change the signature so it won't verify - tampered.certList.SignatureValue.Bytes[0]++ - - verifyTests := []struct { - desc string - crl *CRL - certs []*x509.Certificate - cert *x509.Certificate - errWant string - }{ - { - desc: "Pass intermediate", - crl: loadCRL(t, testdata.Path("crl/1.crl")), - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], - errWant: "", - }, - { - desc: "Pass leaf", - crl: loadCRL(t, testdata.Path("crl/2.crl")), - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[2], - errWant: "", - }, - { - desc: "Fail wrong cert chain", - crl: loadCRL(t, testdata.Path("crl/3.crl")), - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/revokedInt.pem"))[1], - errWant: "No certificates mached", - }, - { - desc: "Fail no certs", - crl: loadCRL(t, testdata.Path("crl/1.crl")), - certs: []*x509.Certificate{}, - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], - errWant: "No certificates mached", - }, - { - desc: "Fail Tampered signature", - crl: tampered, - certs: makeChain(t, testdata.Path("crl/unrevoked.pem")), - cert: makeChain(t, testdata.Path("crl/unrevoked.pem"))[1], - errWant: "verification failure", - }, - } - - for _, tt := range verifyTests { - t.Run(tt.desc, func(t *testing.T) { - err := verifyCRL(tt.crl, tt.cert.RawIssuer, tt.certs) - switch { - case tt.errWant == "" && err != nil: - t.Errorf("Valid CRL did not verify err = %v", err) - case tt.errWant != "" && err == nil: - t.Error("Invalid CRL verified") - case tt.errWant != "" && !strings.Contains(err.Error(), tt.errWant): - t.Errorf("fetchIssuerCRL(_, %v, %v, _) = %v; want Contains(%v)", tt.cert.RawIssuer, tt.certs, err, tt.errWant) - } - }) - } -} - -func TestRevokedCert(t *testing.T) { - revokedIntChain := makeChain(t, testdata.Path("crl/revokedInt.pem")) - revokedLeafChain := makeChain(t, testdata.Path("crl/revokedLeaf.pem")) - validChain := makeChain(t, testdata.Path("crl/unrevoked.pem")) - cache, err := lru.New(5) - if err != nil { - t.Fatalf("lru.New: err = %v", err) - } - - var revocationTests = []struct { - desc string - in tls.ConnectionState - revoked bool - allowUndetermined bool - }{ - { - desc: "Single unrevoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain}}, - revoked: false, - }, - { - desc: "Single revoked intermediate", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedIntChain}}, - revoked: true, - }, - { - desc: "Single revoked leaf", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain}}, - revoked: true, - }, - { - desc: "Multi one revoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, revokedLeafChain}}, - revoked: false, - }, - { - desc: "Multi revoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{revokedLeafChain, revokedIntChain}}, - revoked: true, - }, - { - desc: "Multi unrevoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{validChain, validChain}}, - revoked: false, - }, - { - desc: "Undetermined revoked", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ - {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, - }}, - revoked: true, - }, - { - desc: "Undetermined allowed", - in: tls.ConnectionState{VerifiedChains: [][]*x509.Certificate{ - {&x509.Certificate{CRLDistributionPoints: []string{"test"}}}, - }}, - revoked: false, - allowUndetermined: true, - }, - } - - for _, tt := range revocationTests { - t.Run(tt.desc, func(t *testing.T) { - err := CheckRevocation(tt.in, RevocationConfig{ - RootDir: testdata.Path("crl"), - AllowUndetermined: tt.allowUndetermined, - Cache: cache, - }) - t.Logf("CheckRevocation err = %v", err) - if tt.revoked && err == nil { - t.Error("Revoked certificate chain was allowed") - } else if !tt.revoked && err != nil { - t.Error("Unrevoked certificate not allowed") - } - }) - } -} - -func setupTLSConn(t *testing.T) (net.Listener, *x509.Certificate, *ecdsa.PrivateKey) { - t.Helper() - templ := x509.Certificate{ - SerialNumber: big.NewInt(5), - BasicConstraintsValid: true, - NotBefore: time.Now().Add(-time.Hour), - NotAfter: time.Now().Add(time.Hour), - IsCA: true, - Subject: pkix.Name{CommonName: "test-cert"}, - KeyUsage: x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - IPAddresses: []net.IP{net.ParseIP("::1")}, - CRLDistributionPoints: []string{"http://static.corp.google.com/crl/campus-sln/borg"}, - } - - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatalf("ecdsa.GenerateKey failed err = %v", err) - } - rawCert, err := x509.CreateCertificate(rand.Reader, &templ, &templ, key.Public(), key) - if err != nil { - t.Fatalf("x509.CreateCertificate failed err = %v", err) - } - cert, err := x509.ParseCertificate(rawCert) - if err != nil { - t.Fatalf("x509.ParseCertificate failed err = %v", err) - } - - srvCfg := tls.Config{ - Certificates: []tls.Certificate{ - { - Certificate: [][]byte{cert.Raw}, - PrivateKey: key, - }, - }, - } - l, err := tls.Listen("tcp6", "[::1]:0", &srvCfg) - if err != nil { - t.Fatalf("tls.Listen failed err = %v", err) - } - return l, cert, key -} - -// TestVerifyConnection will setup a client/server connection and check revocation in the real TLS dialer -func TestVerifyConnection(t *testing.T) { - lis, cert, key := setupTLSConn(t) - defer func() { - lis.Close() - }() - - var handshakeTests = []struct { - desc string - revoked []pkix.RevokedCertificate - success bool - }{ - { - desc: "Empty CRL", - revoked: []pkix.RevokedCertificate{}, - success: true, - }, - { - desc: "Revoked Cert", - revoked: []pkix.RevokedCertificate{ - { - SerialNumber: cert.SerialNumber, - RevocationTime: time.Now(), - }, - }, - success: false, - }, - } - for _, tt := range handshakeTests { - t.Run(tt.desc, func(t *testing.T) { - // Accept one connection. - go func() { - conn, err := lis.Accept() - if err != nil { - t.Errorf("tls.Accept failed err = %v", err) - } else { - conn.Write([]byte("Hello, World!")) - conn.Close() - } - }() - - dir, err := os.MkdirTemp("", "crl_dir") - if err != nil { - t.Fatalf("os.MkdirTemp failed err = %v", err) - } - defer os.RemoveAll(dir) - - crl, err := cert.CreateCRL(rand.Reader, key, tt.revoked, time.Now(), time.Now().Add(time.Hour)) - if err != nil { - t.Fatalf("templ.CreateCRL failed err = %v", err) - } - - err = os.WriteFile(path.Join(dir, fmt.Sprintf("%s.r0", x509NameHash(cert.Subject.ToRDNSequence()))), crl, 0777) - if err != nil { - t.Fatalf("os.WriteFile failed err = %v", err) - } - - cp := x509.NewCertPool() - cp.AddCert(cert) - cliCfg := tls.Config{ - RootCAs: cp, - VerifyConnection: func(cs tls.ConnectionState) error { - return CheckRevocation(cs, RevocationConfig{RootDir: dir}) - }, - } - conn, err := tls.Dial(lis.Addr().Network(), lis.Addr().String(), &cliCfg) - t.Logf("tls.Dial err = %v", err) - if tt.success && err != nil { - t.Errorf("Expected success got err = %v", err) - } - if !tt.success && err == nil { - t.Error("Expected error, but got success") - } - if err == nil { - conn.Close() - } - }) - } -} - -func TestIssuerNonPrintableString(t *testing.T) { - rawIssuer, err := hex.DecodeString("300c310a300806022a030c023a29") - if err != nil { - t.Fatalf("failed to decode issuer: %s", err) - } - _, err = fetchCRL(rawIssuer, RevocationConfig{RootDir: testdata.Path("crl")}) - if err != nil { - t.Fatalf("fetchCRL failed: %s", err) - } -} - -// TestCRLCacheExpirationReloading tests the basic expiration and reloading of a -// cached CRL. The setup places an empty CRL in the cache, and a corresponding -// CRL with a revocation in the CRL directory. We then validate the certificate -// to verify that the certificate is not revoked. Then, we modify the -// NextUpdate time to be in the past so that when we next check for revocation, -// the existing cache entry should be seen as expired, and the CRL in the -// directory showing `revokedInt.pem` as revoked will be loaded, resulting in -// the check returning `RevocationRevoked`. -func TestCRLCacheExpirationReloading(t *testing.T) { - cache, err := lru.New(5) - if err != nil { - t.Fatalf("Creating cache failed") - } - - var certs = makeChain(t, testdata.Path("crl/revokedInt.pem")) - // Certs[1] has the same issuer as the revoked cert - rawIssuer := certs[1].RawIssuer - - // `3.crl`` revokes `revokedInt.pem` - crl := loadCRL(t, testdata.Path("crl/3.crl")) - // Modify the crl so that the cert is NOT revoked and add it to the cache - crl.certList.TBSCertList.RevokedCertificates = nil - crl.certList.TBSCertList.NextUpdate = time.Now().Add(time.Hour) - cache.Add(hex.EncodeToString(rawIssuer), crl) - var cfg = RevocationConfig{RootDir: testdata.Path("crl"), Cache: cache} - revocationStatus := checkChain(certs, cfg) - if revocationStatus != RevocationUnrevoked { - t.Fatalf("Certificate check should be RevocationUnrevoked, was %v", revocationStatus) - } - - // Modify the entry in the cache so that the cache will be refreshed - crl.certList.TBSCertList.NextUpdate = time.Now() - cache.Add(hex.EncodeToString(rawIssuer), crl) - - revocationStatus = checkChain(certs, cfg) - if revocationStatus != RevocationRevoked { - t.Fatalf("A certificate should have been `RevocationRevoked` but was %v", revocationStatus) - } -} From 96bf9058e1b9f3202f97744cbb846d5418ffff7e Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 24 Oct 2023 20:50:00 +0000 Subject: [PATCH 54/62] Delete testdate/crl/provider/filewatcher directory and .gitignore under it --- .../advancedtls/testdata/crl/provider/filewatcher/.gitignore | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 security/advancedtls/testdata/crl/provider/filewatcher/.gitignore diff --git a/security/advancedtls/testdata/crl/provider/filewatcher/.gitignore b/security/advancedtls/testdata/crl/provider/filewatcher/.gitignore deleted file mode 100644 index 86d0cb2726c6..000000000000 --- a/security/advancedtls/testdata/crl/provider/filewatcher/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore everything in this directory -* -# Except this file -!.gitignore \ No newline at end of file From 0ce6a2c8cc951375dbf43780d79adc3b831953e7 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Fri, 27 Oct 2023 00:09:56 +0000 Subject: [PATCH 55/62] Race test fix --- security/advancedtls/crl_provider.go | 3 +-- security/advancedtls/crl_provider_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index cd17f4854199..a7587b24536b 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -135,8 +135,7 @@ func (o *FileWatcherOptions) validate() error { return fmt.Errorf("advancedtls: CRLDirectory %v is not readable: %v", o.CRLDirectory, err) } // Checks related to RefreshDuration. - if o.RefreshDuration <= 0 { - grpclogLogger.Infof("RefreshDuration is not set or negative: provided value %v, default value %v will be used.", o.RefreshDuration, defaultCRLRefreshDuration) + if o.RefreshDuration == 0 { o.RefreshDuration = defaultCRLRefreshDuration } if o.RefreshDuration < minCRLRefreshDuration { diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index dc20d8e9c1a8..38de20c07409 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -24,6 +24,7 @@ import ( "io" "os" "path/filepath" + "sync" "testing" "time" @@ -137,9 +138,14 @@ func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { // that it’s correctly processed. Additionally, we also check if number of // invocations of custom callback is correct. func (s) TestFileWatcherCRLProvider(t *testing.T) { + t.Parallel() const nonCRLFilesUnderCRLDirectory = 5 nonCRLFilesSet := make(map[string]struct{}) + //var callbackMutex sync.Mutex + var nonCRLMutex sync.Mutex customCallback := func(err error) { + nonCRLMutex.Lock() + defer nonCRLMutex.Unlock() nonCRLFilesSet[err.Error()] = struct{}{} } p, err := NewFileWatcherCRLProvider(FileWatcherOptions{ @@ -191,9 +197,11 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { } }) } + nonCRLMutex.Lock() if len(nonCRLFilesSet) < nonCRLFilesUnderCRLDirectory { t.Fatalf("Number of callback executions: got %v, want %v", len(nonCRLFilesSet), nonCRLFilesUnderCRLDirectory) } + nonCRLMutex.Unlock() p.Close() } @@ -258,6 +266,8 @@ func (s) TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { t.Run(tt.desc, func(t *testing.T) { copyFiles(sourcePath, targetPath, tt.fileNames, t) p.ScanCRLDirectory() + p.scanMutex.Lock() + defer p.scanMutex.Unlock() if diff := cmp.Diff(len(p.crls), tt.expectedEntries); diff != "" { t.Errorf("Expected number of entries in the map do not match\ndiff (-got +want):\n%s", diff) } From c57a08ab940681b52b87f362438802a94431ec90 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Fri, 27 Oct 2023 17:28:12 +0000 Subject: [PATCH 56/62] Address PR comments --- security/advancedtls/crl_provider_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 38de20c07409..56b67a68afb2 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -141,7 +141,6 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { t.Parallel() const nonCRLFilesUnderCRLDirectory = 5 nonCRLFilesSet := make(map[string]struct{}) - //var callbackMutex sync.Mutex var nonCRLMutex sync.Mutex customCallback := func(err error) { nonCRLMutex.Lock() @@ -197,12 +196,11 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { } }) } - nonCRLMutex.Lock() + p.Close() if len(nonCRLFilesSet) < nonCRLFilesUnderCRLDirectory { t.Fatalf("Number of callback executions: got %v, want %v", len(nonCRLFilesSet), nonCRLFilesUnderCRLDirectory) } - nonCRLMutex.Unlock() - p.Close() + } // TestFileWatcherCRLProviderDirectoryScan tests how FileWatcherCRLProvider From 4c53c565f555be37e1f3c4faf67efcad983e2d79 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Fri, 27 Oct 2023 18:29:41 +0000 Subject: [PATCH 57/62] Address PR comments --- security/advancedtls/crl_provider_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 56b67a68afb2..c39b10d6142f 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -24,7 +24,6 @@ import ( "io" "os" "path/filepath" - "sync" "testing" "time" @@ -138,13 +137,9 @@ func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { // that it’s correctly processed. Additionally, we also check if number of // invocations of custom callback is correct. func (s) TestFileWatcherCRLProvider(t *testing.T) { - t.Parallel() const nonCRLFilesUnderCRLDirectory = 5 nonCRLFilesSet := make(map[string]struct{}) - var nonCRLMutex sync.Mutex customCallback := func(err error) { - nonCRLMutex.Lock() - defer nonCRLMutex.Unlock() nonCRLFilesSet[err.Error()] = struct{}{} } p, err := NewFileWatcherCRLProvider(FileWatcherOptions{ @@ -200,7 +195,6 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { if len(nonCRLFilesSet) < nonCRLFilesUnderCRLDirectory { t.Fatalf("Number of callback executions: got %v, want %v", len(nonCRLFilesSet), nonCRLFilesUnderCRLDirectory) } - } // TestFileWatcherCRLProviderDirectoryScan tests how FileWatcherCRLProvider From 7fedab527c0d3ab7921abcfd5cdac9704f48f94b Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Sat, 28 Oct 2023 03:19:35 +0000 Subject: [PATCH 58/62] Refactor directory reloader test from checking size of crl map to querying individual entries approach --- security/advancedtls/crl_provider_test.go | 106 ++++++++++++++-------- 1 file changed, 70 insertions(+), 36 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index c39b10d6142f..a30ab9dad1d9 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -19,6 +19,7 @@ package advancedtls import ( + "crypto/tls" "crypto/x509" "fmt" "io" @@ -137,7 +138,7 @@ func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { // that it’s correctly processed. Additionally, we also check if number of // invocations of custom callback is correct. func (s) TestFileWatcherCRLProvider(t *testing.T) { - const nonCRLFilesUnderCRLDirectory = 5 + const nonCRLFilesUnderCRLDirectory = 15 nonCRLFilesSet := make(map[string]struct{}) customCallback := func(err error) { nonCRLFilesSet[err.Error()] = struct{}{} @@ -192,8 +193,8 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { }) } p.Close() - if len(nonCRLFilesSet) < nonCRLFilesUnderCRLDirectory { - t.Fatalf("Number of callback executions: got %v, want %v", len(nonCRLFilesSet), nonCRLFilesUnderCRLDirectory) + if diff := cmp.Diff(len(nonCRLFilesSet), nonCRLFilesUnderCRLDirectory); diff != "" { + t.Errorf("Unexpected number Number of callback executions\ndiff (-got +want):\n%s", diff) } } @@ -218,54 +219,78 @@ func (s) TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { // of the files to be copied there before the test case execution, and // expected number of entries in the FileWatcherCRLProvider map. tests := []struct { - desc string - fileNames []string - expectedEntries int + desc string + crlFileNames []string + certFileNames []struct { + fileName string + expected bool + } }{ { - desc: "Empty dir", - fileNames: []string{}, - expectedEntries: 0, + desc: "Simple addition (1 map entry)", + crlFileNames: []string{"1.crl"}, + certFileNames: []struct { + fileName string + expected bool + }{ + {"crl/unrevoked.pem", true}, + }, }, { - desc: "Simple addition", - fileNames: []string{"1.crl"}, - expectedEntries: 1, + desc: "Addition and deletion (2 map entries)", + crlFileNames: []string{"3.crl", "5.crl"}, + certFileNames: []struct { + fileName string + expected bool + }{ + {"crl/revokedInt.pem", true}, + {"crl/revokedLeaf.pem", true}, + {"crl/unrevoked.pem", false}, + }, }, { - desc: "Addition and deletion", - fileNames: []string{"2.crl", "3.crl"}, - expectedEntries: 2, - }, + desc: "Addition and a corrupt file (3 map entries)", + crlFileNames: []string{"1.crl", "README.md"}, + certFileNames: []struct { + fileName string + expected bool + }{ + {"crl/revokedInt.pem", true}, + {"crl/revokedLeaf.pem", true}, + {"crl/unrevoked.pem", true}, + }}, { - desc: "Addition and updating", - fileNames: []string{"2.crl", "3.crl", "4.crl"}, - expectedEntries: 3, - }, - { - desc: "Addition and a corrupt file", - fileNames: []string{"5.crl", "README.md"}, - expectedEntries: 4, - }, - { - desc: "Full deletion", - fileNames: []string{}, - expectedEntries: 0, - }, + desc: "Full deletion (0 map entries)", + crlFileNames: []string{}, + certFileNames: []struct { + fileName string + expected bool + }{ + {"crl/revokedInt.pem", false}, + {"crl/revokedLeaf.pem", false}, + {"crl/unrevoked.pem", false}, + }}, } for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { - copyFiles(sourcePath, targetPath, tt.fileNames, t) + copyFiles(sourcePath, targetPath, tt.crlFileNames, t) p.ScanCRLDirectory() - p.scanMutex.Lock() - defer p.scanMutex.Unlock() - if diff := cmp.Diff(len(p.crls), tt.expectedEntries); diff != "" { - t.Errorf("Expected number of entries in the map do not match\ndiff (-got +want):\n%s", diff) + for _, certFileName := range tt.certFileNames { + c := makeChain(t, testdata.Path(certFileName.fileName))[0] + crl, err := p.CRL(c) + if err != nil { + t.Errorf("Cannot fetch CRL from provider: %v", err) + } + if crl == nil && certFileName.expected { + t.Errorf("CRL is unexpectedly nil") + } + if crl != nil && !certFileName.expected { + t.Errorf("CRL is unexpectedly not nil") + } } }) } - p.Close() } @@ -320,3 +345,12 @@ func createTmpDir(t *testing.T) string { t.Logf("Using tmpdir: %s", dir) return dir } + +func loadCert(t *testing.T, certPath, key string) tls.Certificate { + t.Helper() + cert, err := tls.LoadX509KeyPair(testdata.Path(certPath), testdata.Path(key)) + if err != nil { + t.Fatalf("tls.LoadX509KeyPair(%q, %q) failed: %v", certPath, key, err) + } + return cert +} From 10253336d1d6c3186771081444fb1bf3855a8081 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Sat, 28 Oct 2023 03:30:51 +0000 Subject: [PATCH 59/62] Add extra case for RefreshDuration config test --- security/advancedtls/crl_provider_test.go | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index a30ab9dad1d9..811f51075db3 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -19,7 +19,6 @@ package advancedtls import ( - "crypto/tls" "crypto/x509" "fmt" "io" @@ -122,12 +121,15 @@ func (s) TestFileWatcherCRLProviderConfig(t *testing.T) { } regularProvider, err := NewFileWatcherCRLProvider(FileWatcherOptions{ CRLDirectory: testdata.Path("crl"), - RefreshDuration: 1 * time.Hour, + RefreshDuration: 2 * time.Hour, CRLReloadingFailedCallback: customCallback, }) if err != nil { t.Fatal("Unexpected error while creating regular FileWatcherCRLProvider:", err) } + if regularProvider.opts.RefreshDuration != 2*time.Hour { + t.Fatalf("Valid refreshDuration was incorrectly updated by validate() func") + } regularProvider.Close() } @@ -345,12 +347,3 @@ func createTmpDir(t *testing.T) string { t.Logf("Using tmpdir: %s", dir) return dir } - -func loadCert(t *testing.T, certPath, key string) tls.Certificate { - t.Helper() - cert, err := tls.LoadX509KeyPair(testdata.Path(certPath), testdata.Path(key)) - if err != nil { - t.Fatalf("tls.LoadX509KeyPair(%q, %q) failed: %v", certPath, key, err) - } - return cert -} From d9ba363c28227f9d0d47cdf9fec4b4a3ad832b4c Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 30 Oct 2023 15:25:12 +0000 Subject: [PATCH 60/62] Update cpmment for table test structure --- security/advancedtls/crl_provider_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 811f51075db3..7b6c7d0e05b3 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -217,9 +217,11 @@ func (s) TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) } - // Each test data entry contains a description of CRL directory content, name - // of the files to be copied there before the test case execution, and - // expected number of entries in the FileWatcherCRLProvider map. + // Each test data entry contains a description of CRL directory content + // (including the expected number of entries in the FileWatcherCRLProvider + // map), the name of the files to be copied there before executing the test + // case, and information regarding whether a specific certificate is expected + // to be found in the map. tests := []struct { desc string crlFileNames []string From 150e585741083284dfcd6ed1da9497c2196adbdf Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Mon, 30 Oct 2023 18:09:16 +0000 Subject: [PATCH 61/62] Unexport scan scanCRLDirectory, drop related mutex, update the comments --- security/advancedtls/crl_provider.go | 29 ++++++++++------------- security/advancedtls/crl_provider_test.go | 5 ++-- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index a7587b24536b..28e725149188 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -100,12 +100,11 @@ type FileWatcherOptions struct { // in-memory. Users should call Close to stop the background refresh of // CRLDirectory. type FileWatcherCRLProvider struct { - crls map[string]*CRL - opts FileWatcherOptions - mu sync.Mutex - scanMutex sync.Mutex - stop chan struct{} - done chan struct{} + crls map[string]*CRL + opts FileWatcherOptions + mu sync.Mutex + stop chan struct{} + done chan struct{} } // NewFileWatcherCRLProvider returns a new instance of the @@ -150,7 +149,7 @@ func (p *FileWatcherCRLProvider) run() { defer close(p.done) ticker := time.NewTicker(p.opts.RefreshDuration) defer ticker.Stop() - p.ScanCRLDirectory() + p.scanCRLDirectory() for { select { @@ -158,27 +157,25 @@ func (p *FileWatcherCRLProvider) run() { grpclogLogger.Infof("Scanning of CRLDirectory %v stopped", p.opts.CRLDirectory) return case <-ticker.C: - p.ScanCRLDirectory() + p.scanCRLDirectory() } } } -// Close stops the background refresh of CRLDirectory of FileWatcherCRLProvider. +// Close waits till the background refresh of CRLDirectory of +// FileWatcherCRLProvider is done and then stops it. func (p *FileWatcherCRLProvider) Close() { close(p.stop) <-p.done } -// ScanCRLDirectory starts the process of scanning +// scanCRLDirectory starts the process of scanning // FileWatcherOptions.CRLDirectory and updating in-memory storage of CRL -// structs, as defined in [gRFC A69]. Users should not call this function in a -// loop since it's called periodically (see FileWatcherOptions.RefreshDuration) -// by run goroutine. +// structs, as defined in [gRFC A69]. It's called periodically +// (see FileWatcherOptions.RefreshDuration) by run goroutine. // // [gRFC A69]: https://github.com/grpc/proposal/pull/382 -func (p *FileWatcherCRLProvider) ScanCRLDirectory() { - p.scanMutex.Lock() - defer p.scanMutex.Unlock() +func (p *FileWatcherCRLProvider) scanCRLDirectory() { dir, err := os.Open(p.opts.CRLDirectory) if err != nil { grpclogLogger.Errorf("Can't open CRLDirectory %v", p.opts.CRLDirectory, err) diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 7b6c7d0e05b3..90f85b1c40e1 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -153,7 +153,7 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { if err != nil { t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) } - p.ScanCRLDirectory() + p.Close() // Each test data entry contains a description of a certificate chain, // certificate chain itself, and if CRL is not expected to be found. @@ -194,7 +194,6 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { } }) } - p.Close() if diff := cmp.Diff(len(nonCRLFilesSet), nonCRLFilesUnderCRLDirectory); diff != "" { t.Errorf("Unexpected number Number of callback executions\ndiff (-got +want):\n%s", diff) } @@ -279,7 +278,7 @@ func (s) TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { for _, tt := range tests { t.Run(tt.desc, func(t *testing.T) { copyFiles(sourcePath, targetPath, tt.crlFileNames, t) - p.ScanCRLDirectory() + p.scanCRLDirectory() for _, certFileName := range tt.certFileNames { c := makeChain(t, testdata.Path(certFileName.fileName))[0] crl, err := p.CRL(c) From d7cf48f71e11a27b5881ba1ff29fbae9ea1e55a6 Mon Sep 17 00:00:00 2001 From: Andrey Ermolov Date: Tue, 31 Oct 2023 00:24:43 +0000 Subject: [PATCH 62/62] Update API comments, clear tmp dir after the tests --- security/advancedtls/crl_provider.go | 3 ++- security/advancedtls/crl_provider_test.go | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/security/advancedtls/crl_provider.go b/security/advancedtls/crl_provider.go index 28e725149188..590906169f35 100644 --- a/security/advancedtls/crl_provider.go +++ b/security/advancedtls/crl_provider.go @@ -229,7 +229,8 @@ func (p *FileWatcherCRLProvider) scanCRLDirectory() { } // CRL retrieves the CRL associated with the given certificate's issuer DN from -// in-memory if it was previously loaded during CRLDirectory scan. +// in-memory if it was loaded during FileWatcherOptions.CRLDirectory scan before +// the execution of this function. func (p *FileWatcherCRLProvider) CRL(cert *x509.Certificate) (*CRL, error) { p.mu.Lock() defer p.mu.Unlock() diff --git a/security/advancedtls/crl_provider_test.go b/security/advancedtls/crl_provider_test.go index 90f85b1c40e1..5245f58895ab 100644 --- a/security/advancedtls/crl_provider_test.go +++ b/security/advancedtls/crl_provider_test.go @@ -153,6 +153,9 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { if err != nil { t.Fatal("Unexpected error while creating FileWatcherCRLProvider:", err) } + + // We need to make sure that initial CRLDirectory scan is completed before + // querying the internal map. p.Close() // Each test data entry contains a description of a certificate chain, @@ -208,6 +211,7 @@ func (s) TestFileWatcherCRLProvider(t *testing.T) { func (s) TestFileWatcherCRLProviderDirectoryScan(t *testing.T) { sourcePath := testdata.Path("crl") targetPath := createTmpDir(t) + defer os.RemoveAll(targetPath) p, err := NewFileWatcherCRLProvider(FileWatcherOptions{ CRLDirectory: targetPath, RefreshDuration: 1 * time.Hour,