Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: CRL cache #216

Merged
merged 119 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
119 commits
Select commit Hold shift + click to select a range
62aa8fc
Squashed commit of the following:
JeyJeyGao Nov 29, 2023
eef8579
test: add unit test
JeyJeyGao Dec 7, 2023
feb151c
feat: crl support
JeyJeyGao Apr 18, 2024
56da59c
fix: update
JeyJeyGao Apr 18, 2024
7d869a7
Merge remote-tracking branch 'upstream/main' into feat/crl
JeyJeyGao Apr 18, 2024
a25275e
fix: update
JeyJeyGao May 29, 2024
a262bab
fix: update crl
JeyJeyGao Jul 8, 2024
7d591d6
Merge remote-tracking branch 'upstream/main' into feat/crl
JeyJeyGao Jul 8, 2024
e66d9fd
fix: update cache
JeyJeyGao Jul 11, 2024
ac1d240
fix: update
JeyJeyGao Jul 12, 2024
e789f29
Merge remote-tracking branch 'upstream/main' into feat/crl
JeyJeyGao Jul 12, 2024
31fa5a5
fix: update test
JeyJeyGao Jul 12, 2024
039be39
fix: update
JeyJeyGao Jul 12, 2024
3b1a7ed
fix: update
JeyJeyGao Jul 12, 2024
6875035
fix: update
JeyJeyGao Jul 12, 2024
5bbd8c7
fix: update
JeyJeyGao Jul 15, 2024
39c94d3
fix: deprecate revocation Mode
JeyJeyGao Jul 15, 2024
c85d472
fix: update
JeyJeyGao Jul 15, 2024
a8db155
fix: update
JeyJeyGao Jul 15, 2024
534d11e
fix: update
JeyJeyGao Jul 16, 2024
50964ba
fix: update
JeyJeyGao Jul 16, 2024
cf593b1
fix: update
JeyJeyGao Jul 19, 2024
c172845
fix: update
JeyJeyGao Jul 19, 2024
1aa53fd
fix: update
JeyJeyGao Jul 19, 2024
2d39c35
fix: update
JeyJeyGao Jul 19, 2024
4c01b9e
fix: update
JeyJeyGao Jul 19, 2024
5787e75
fix: remove cache
JeyJeyGao Jul 22, 2024
41746fb
fix: update
JeyJeyGao Jul 22, 2024
759b727
fix: refactor
JeyJeyGao Jul 22, 2024
17da500
fix: update
JeyJeyGao Jul 23, 2024
75884a5
fix: complete test for crl package
JeyJeyGao Jul 23, 2024
e92aff7
fix: update
JeyJeyGao Jul 23, 2024
5c5c041
fix: update github action rule for branches
JeyJeyGao Jul 24, 2024
efe7708
fix: update
JeyJeyGao Jul 24, 2024
28bbf22
fix: update
JeyJeyGao Jul 24, 2024
a8f3b4b
fix: update
JeyJeyGao Jul 24, 2024
93ee863
fix: update
JeyJeyGao Jul 24, 2024
99ad04c
fix: update
JeyJeyGao Jul 24, 2024
9eb5af5
fix: update
JeyJeyGao Jul 24, 2024
38d04ce
fix: update
JeyJeyGao Jul 24, 2024
6a5357e
fix: update
JeyJeyGao Jul 24, 2024
3f9a259
fix: update
JeyJeyGao Jul 25, 2024
6bf3ca2
fix: update
JeyJeyGao Jul 25, 2024
751397b
fix: add CRL size limit
JeyJeyGao Jul 25, 2024
3170db3
fix: restore workflow and update NewWithOptions
JeyJeyGao Jul 25, 2024
8df836d
Merge branch 'main' into feat/crl_no_cache
JeyJeyGao Jul 25, 2024
041a63d
fix: update
JeyJeyGao Jul 25, 2024
e170b90
fix: update
JeyJeyGao Jul 25, 2024
1c68a17
fix: update
JeyJeyGao Jul 26, 2024
a56eb87
Squashed commit of the following:
JeyJeyGao Nov 29, 2023
5e5ddf3
test: add unit test
JeyJeyGao Dec 7, 2023
d1976f3
feat: crl cache
JeyJeyGao Jul 29, 2024
28440d4
Merge branch 'feat/crl_no_cache' into feat/crl_cache
JeyJeyGao Jul 29, 2024
8f00620
fix: update
JeyJeyGao Jul 29, 2024
6e82a2f
fix: update cache
JeyJeyGao Aug 1, 2024
1dc7d9a
fix: update
JeyJeyGao Aug 1, 2024
f50a9c3
fix: update
JeyJeyGao Aug 1, 2024
cb771bb
fix: update
JeyJeyGao Aug 1, 2024
f79d4a3
fix: update
JeyJeyGao Aug 2, 2024
5bee5b6
fix: update
JeyJeyGao Aug 2, 2024
383e22e
fix: update
JeyJeyGao Aug 2, 2024
1ae6d1c
fix: update
JeyJeyGao Aug 8, 2024
87d618a
fix: update
JeyJeyGao Aug 8, 2024
e128f6d
fix: update
JeyJeyGao Aug 9, 2024
0caff0b
test: add unit test for bundle.go
JeyJeyGao Aug 9, 2024
51ef3bf
fix: update
JeyJeyGao Aug 9, 2024
c7a54cd
test: add unit test for memory cache
JeyJeyGao Aug 9, 2024
20b8189
test: update
JeyJeyGao Aug 9, 2024
7300ffd
fix: update license
JeyJeyGao Aug 9, 2024
0d5c99f
fix: update
JeyJeyGao Aug 9, 2024
412cdfe
Merge remote-tracking branch 'upstream/main' into feat/crl_cache
JeyJeyGao Aug 9, 2024
f5110f9
fix: update
JeyJeyGao Aug 9, 2024
b45fa97
fix: resolve comments
JeyJeyGao Aug 12, 2024
78839ca
fix: update
JeyJeyGao Aug 12, 2024
089124c
fix: update
JeyJeyGao Aug 12, 2024
8a30ada
fix: update
JeyJeyGao Aug 14, 2024
5dcd1f1
test: add unit test
JeyJeyGao Aug 14, 2024
b8fb823
fix: update
JeyJeyGao Aug 20, 2024
291cdb7
fix: update
JeyJeyGao Aug 20, 2024
45215ca
Merge branch 'main' into feat/crl_cache
JeyJeyGao Sep 2, 2024
ec44730
merge: CRL
JeyJeyGao Sep 14, 2024
09c668a
fix: update
JeyJeyGao Sep 14, 2024
164a83b
Merge branch 'feat/crl_cache' of https://github.com/JeyJeyGao/notatio…
JeyJeyGao Sep 14, 2024
a88cd4e
fix: update
JeyJeyGao Sep 14, 2024
e3edb43
Merge remote-tracking branch 'upstream/main' into feat/crl_cache
JeyJeyGao Sep 18, 2024
0d64aed
fix: update
JeyJeyGao Sep 18, 2024
75f5f6c
fix: update
JeyJeyGao Sep 18, 2024
617bd7f
fix: update
JeyJeyGao Sep 18, 2024
0653b0b
fix: update
JeyJeyGao Sep 18, 2024
cf4bb29
fix: update
JeyJeyGao Sep 18, 2024
50fd7e1
fix: update
JeyJeyGao Sep 18, 2024
65345ba
fix: update
JeyJeyGao Sep 18, 2024
5637752
docs: add license
JeyJeyGao Sep 18, 2024
e9f3b9d
fix: update
JeyJeyGao Sep 18, 2024
a3c3c84
fix: remove cache implementation
JeyJeyGao Sep 18, 2024
db796ba
fix: remove Using function
JeyJeyGao Sep 19, 2024
7572b7f
fix: update comment
JeyJeyGao Sep 19, 2024
fa816d2
fix: remove max age
JeyJeyGao Sep 19, 2024
53e88a0
fix: update
JeyJeyGao Sep 19, 2024
d7371a6
fix: update
JeyJeyGao Sep 19, 2024
2df6f2f
fix: resolve comments
JeyJeyGao Sep 19, 2024
6267143
fix: update
JeyJeyGao Sep 19, 2024
9f8c8ce
test: improve test coverage
JeyJeyGao Sep 19, 2024
4bd6d06
fix: update comment
JeyJeyGao Sep 19, 2024
4d19eb6
fix: update
JeyJeyGao Sep 19, 2024
8356d24
fix: resolve comments
JeyJeyGao Sep 19, 2024
b466b43
fix: update
JeyJeyGao Sep 19, 2024
95e8590
fix: update
JeyJeyGao Sep 19, 2024
d78d089
fix: update
JeyJeyGao Sep 19, 2024
dae3a2a
fix: resolve comments
JeyJeyGao Sep 19, 2024
73c2958
fix: update
JeyJeyGao Sep 19, 2024
7ed8899
fix: resolve comments
JeyJeyGao Sep 19, 2024
00e8bce
fix: added DiscardCacheError in fetcher
JeyJeyGao Sep 20, 2024
335d7d9
fix: update
JeyJeyGao Sep 20, 2024
cd5bbc0
fix: update
JeyJeyGao Sep 20, 2024
f21a22e
test: update
JeyJeyGao Sep 20, 2024
375867e
fix: update
JeyJeyGao Sep 20, 2024
6741002
fix: update
JeyJeyGao Sep 20, 2024
4b3bd0e
fix: update
JeyJeyGao Sep 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions revocation/crl/bundle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright The Notary Project 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 crl

import "crypto/x509"

// Bundle is a collection of CRLs, including base and delta CRLs
type Bundle struct {
// BaseCRL is the parsed base CRL
BaseCRL *x509.RevocationList

// DeltaCRL is the parsed delta CRL
//
// TODO: support delta CRL https://github.com/notaryproject/notation-core-go/issues/228
// It will always be nil until we support delta CRL
DeltaCRL *x509.RevocationList
}
32 changes: 32 additions & 0 deletions revocation/crl/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright The Notary Project 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 crl

import "context"

// Cache is an interface that specifies methods used for caching
type Cache interface {
// Get retrieves the CRL bundle with the given url
//
// url is the key to retrieve the CRL bundle
//
// if the key does not exist or the content is expired, return ErrCacheMiss.
Get(ctx context.Context, url string) (*Bundle, error)

// Set stores the CRL bundle with the given url
//
// url is the key to store the CRL bundle
// bundle is the CRL collections to store
Set(ctx context.Context, url string, bundle *Bundle) error
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,5 @@ package crl

import "errors"

var (
// ErrDeltaCRLNotSupported is returned when the CRL contains a delta CRL but
// the delta CRL is not supported.
ErrDeltaCRLNotSupported = errors.New("delta CRL is not supported")
)
// ErrCacheMiss is returned when a cache miss occurs.
var ErrCacheMiss = errors.New("cache miss")
167 changes: 167 additions & 0 deletions revocation/crl/fetcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Copyright The Notary Project 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 crl provides Fetcher interface with its implementation, and the
// Cache interface.
package crl

import (
"context"
"crypto/x509"
"encoding/asn1"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"time"
)

// oidFreshestCRL is the object identifier for the distribution point
// for the delta CRL. (See RFC 5280, Section 5.2.6)
var oidFreshestCRL = asn1.ObjectIdentifier{2, 5, 29, 46}

// maxCRLSize is the maximum size of CRL in bytes
//
// The 32 MiB limit is based on investigation that even the largest CRLs
// are less than 16 MiB. The limit is set to 32 MiB to prevent
const maxCRLSize = 32 * 1024 * 1024 // 32 MiB

// Fetcher is an interface that specifies methods used for fetching CRL
// from the given URL
type Fetcher interface {
// Fetch retrieves the CRL from the given URL.
Fetch(ctx context.Context, url string) (*Bundle, error)
}

// HTTPFetcher is a Fetcher implementation that fetches CRL from the given URL
type HTTPFetcher struct {
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
// Cache stores fetched CRLs and reuses them until the CRL reaches the
// NextUpdate time.
// If Cache is nil, no cache is used.
Cache Cache

// DiscardCacheError specifies whether to discard any error on cache.
//
// ErrCacheMiss is not considered as an failure and will not be returned as
// an error if DiscardCacheError is false.
DiscardCacheError bool

httpClient *http.Client
}

// NewHTTPFetcher creates a new HTTPFetcher with the given HTTP client
func NewHTTPFetcher(httpClient *http.Client) (*HTTPFetcher, error) {
if httpClient == nil {
return nil, errors.New("httpClient cannot be nil")
}

return &HTTPFetcher{
httpClient: httpClient,
}, nil
}

// Fetch retrieves the CRL from the given URL
//
// If cache is not nil, try to get the CRL from the cache first. On failure
// (e.g. cache miss), it will download the CRL from the URL and store it to the
// cache.
func (f *HTTPFetcher) Fetch(ctx context.Context, url string) (*Bundle, error) {
if url == "" {
return nil, errors.New("CRL URL cannot be empty")
}

if f.Cache != nil {
bundle, err := f.Cache.Get(ctx, url)
if err == nil {
// check expiry
nextUpdate := bundle.BaseCRL.NextUpdate
if !nextUpdate.IsZero() && !time.Now().After(nextUpdate) {
Two-Hearts marked this conversation as resolved.
Show resolved Hide resolved
return bundle, nil
}
} else if !errors.Is(err, ErrCacheMiss) && !f.DiscardCacheError {
return nil, fmt.Errorf("failed to retrieve CRL from cache: %w", err)
}
}

bundle, err := f.fetch(ctx, url)
if err != nil {
return nil, fmt.Errorf("failed to retrieve CRL: %w", err)
}

if f.Cache != nil {
err = f.Cache.Set(ctx, url, bundle)
if err != nil && !f.DiscardCacheError {
return nil, fmt.Errorf("failed to store CRL to cache: %w", err)
}
}

return bundle, nil
}

// fetch downloads the CRL from the given URL.
func (f *HTTPFetcher) fetch(ctx context.Context, url string) (*Bundle, error) {
// fetch base CRL
base, err := fetchCRL(ctx, url, f.httpClient)
if err != nil {
return nil, err
}

// check delta CRL
// TODO: support delta CRL https://github.com/notaryproject/notation-core-go/issues/228
for _, ext := range base.Extensions {
if ext.Id.Equal(oidFreshestCRL) {
return nil, errors.New("delta CRL is not supported")
}
}

return &Bundle{
BaseCRL: base,
}, nil
}

func fetchCRL(ctx context.Context, crlURL string, client *http.Client) (*x509.RevocationList, error) {
// validate URL
parsedURL, err := url.Parse(crlURL)
if err != nil {
return nil, fmt.Errorf("invalid CRL URL: %w", err)
}
if parsedURL.Scheme != "http" {
return nil, fmt.Errorf("unsupported scheme: %s. Only supports CRL URL in HTTP protocol", parsedURL.Scheme)
}

// download CRL
req, err := http.NewRequestWithContext(ctx, http.MethodGet, crlURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create CRL request: %w", err)
}
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("failed to download with status code: %d", resp.StatusCode)
}
// read with size limit
data, err := io.ReadAll(io.LimitReader(resp.Body, maxCRLSize))
if err != nil {
return nil, fmt.Errorf("failed to read CRL response: %w", err)
}
if len(data) == maxCRLSize {
return nil, fmt.Errorf("CRL size exceeds the limit: %d", maxCRLSize)
}

// parse CRL
return x509.ParseRevocationList(data)
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading