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: delta CRL #247

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e34370f
feat: delta CRL
JeyJeyGao Nov 26, 2024
8182be9
fix: license and variable names
JeyJeyGao Nov 26, 2024
2049193
fix: optimize logic
JeyJeyGao Nov 26, 2024
33d5590
fix: working on delta CRL
JeyJeyGao Nov 27, 2024
7984f57
fix: bug
JeyJeyGao Nov 27, 2024
77b5972
test: add test for fetcher
JeyJeyGao Nov 28, 2024
0b6179a
test: complete test for validate
JeyJeyGao Nov 28, 2024
c9f936f
test: add test cases
JeyJeyGao Nov 28, 2024
c228487
Merge branch 'main' into feat/delta_crl
JeyJeyGao Nov 28, 2024
483d298
fix: only support delta CRL url from base CRL
JeyJeyGao Dec 5, 2024
2910e31
fix: update
JeyJeyGao Dec 5, 2024
934698a
fix: improve code style
JeyJeyGao Dec 9, 2024
c506b40
Merge branch 'main' into feat/delta_crl
JeyJeyGao Dec 9, 2024
cc34d0d
fix: update
JeyJeyGao Dec 9, 2024
630c7a5
fix: simplify code
JeyJeyGao Dec 9, 2024
ad51cf8
fix: simplify code
JeyJeyGao Dec 9, 2024
a9330e5
fix: update
JeyJeyGao Dec 13, 2024
b2f2852
fix: optimize Fetch
JeyJeyGao Dec 13, 2024
1239c5c
fix: update
JeyJeyGao Dec 13, 2024
28069ee
fix: resolve comment for Shiwei
JeyJeyGao Dec 16, 2024
6becbe1
fix: update
JeyJeyGao Dec 16, 2024
43e6407
fix: resolve comments for Two-Hearts
JeyJeyGao Dec 23, 2024
52c48a8
fix: add license
JeyJeyGao Dec 23, 2024
0e5b4d0
fix: simplify code
JeyJeyGao Dec 24, 2024
dc3a62e
fix: test
JeyJeyGao Dec 24, 2024
9c5f919
fix: resolve comment for Shiwei
JeyJeyGao Dec 26, 2024
a24fe9d
fix: update comment
JeyJeyGao Dec 26, 2024
31bdd6a
Merge branch 'main' into feat/delta_crl
JeyJeyGao Jan 2, 2025
3de709f
Merge branch 'main' into feat/delta_crl
JeyJeyGao Jan 7, 2025
e49e0de
Merge branch 'main' into feat/delta_crl
JeyJeyGao Jan 14, 2025
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
3 changes: 3 additions & 0 deletions revocation/crl/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ import "errors"

// ErrCacheMiss is returned when a cache miss occurs.
var ErrCacheMiss = errors.New("cache miss")

// errDeltaCRLNotFound is returned when a delta CRL is not found.
var errDeltaCRLNotFound = errors.New("delta CRL not found")
119 changes: 109 additions & 10 deletions revocation/crl/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@
import (
"context"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"time"

"github.com/notaryproject/notation-core-go/revocation/internal/x509util"
"golang.org/x/crypto/cryptobyte"
cbasn1 "golang.org/x/crypto/cryptobyte/asn1"
)

// oidFreshestCRL is the object identifier for the distribution point
Expand Down Expand Up @@ -84,9 +89,8 @@
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) {
// check expiry of base CRL and delta CRL
if isEffective(bundle.BaseCRL) && (bundle.DeltaCRL == nil || isEffective(bundle.DeltaCRL)) {
return bundle, nil
}
} else if !errors.Is(err, ErrCacheMiss) && !f.DiscardCacheError {
Expand All @@ -109,6 +113,11 @@
return bundle, nil
}

// isEffective checks if the CRL is effective by checking the NextUpdate time.
func isEffective(crl *x509.RevocationList) bool {
return !crl.NextUpdate.IsZero() && !time.Now().After(crl.NextUpdate)
}

// fetch downloads the CRL from the given URL.
func (f *HTTPFetcher) fetch(ctx context.Context, url string) (*Bundle, error) {
// fetch base CRL
Expand All @@ -117,19 +126,109 @@
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")
}
// fetch delta CRL from base CRL extension
deltaCRL, err := f.fetchDeltaCRL(ctx, base.Extensions)
if err != nil && !errors.Is(err, errDeltaCRLNotFound) {
return nil, err
}

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

// fetchDeltaCRL fetches the delta CRL from the given extensions of base CRL.
//
// It returns errDeltaCRLNotFound if the delta CRL is not found.
func (f *HTTPFetcher) fetchDeltaCRL(ctx context.Context, extensions []pkix.Extension) (*x509.RevocationList, error) {
extension := x509util.FindExtensionByOID(oidFreshestCRL, extensions)
if extension == nil {
return nil, errDeltaCRLNotFound
}

// RFC 5280, 4.2.1.15
// id-ce-freshestCRL OBJECT IDENTIFIER ::= { id-ce 46 }
//
// FreshestCRL ::= CRLDistributionPoints
urls, err := parseCRLDistributionPoint(extension.Value)
if err != nil {
return nil, fmt.Errorf("failed to parse Freshest CRL extension: %w", err)
}
if len(urls) == 0 {
return nil, errDeltaCRLNotFound
}

var (
lastError error
deltaCRL *x509.RevocationList
)
for _, cdpURL := range urls {
// RFC 5280, 5.2.6
// Delta CRLs from the base CRL have the same scope as the base
// CRL, so the URLs are for redundancy and should be tried in
// order until one succeeds.
deltaCRL, lastError = fetchCRL(ctx, cdpURL, f.httpClient)
if lastError == nil {
return deltaCRL, nil
}
}
return nil, lastError
}

// parseCRLDistributionPoint parses the CRL extension and returns the CRL URLs
//
// value is the raw value of the CRL distribution point extension
func parseCRLDistributionPoint(value []byte) ([]string, error) {
var urls []string
// borrowed from crypto/x509: https://cs.opensource.google/go/go/+/refs/tags/go1.23.4:src/crypto/x509/parser.go;l=700-743
//
// RFC 5280, 4.2.1.13
//
// CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint
//
// DistributionPoint ::= SEQUENCE {
// distributionPoint [0] DistributionPointName OPTIONAL,
// reasons [1] ReasonFlags OPTIONAL,
// cRLIssuer [2] GeneralNames OPTIONAL }
//
// DistributionPointName ::= CHOICE {
// fullName [0] GeneralNames,
// nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
val := cryptobyte.String(value)
if !val.ReadASN1(&val, cbasn1.SEQUENCE) {
return nil, errors.New("x509: invalid CRL distribution points")
}
for !val.Empty() {
var dpDER cryptobyte.String
if !val.ReadASN1(&dpDER, cbasn1.SEQUENCE) {
return nil, errors.New("x509: invalid CRL distribution point")
}

Check warning on line 206 in revocation/crl/fetcher.go

View check run for this annotation

Codecov / codecov/patch

revocation/crl/fetcher.go#L205-L206

Added lines #L205 - L206 were not covered by tests
var dpNameDER cryptobyte.String
var dpNamePresent bool
if !dpDER.ReadOptionalASN1(&dpNameDER, &dpNamePresent, cbasn1.Tag(0).Constructed().ContextSpecific()) {
return nil, errors.New("x509: invalid CRL distribution point")
}

Check warning on line 211 in revocation/crl/fetcher.go

View check run for this annotation

Codecov / codecov/patch

revocation/crl/fetcher.go#L210-L211

Added lines #L210 - L211 were not covered by tests
if !dpNamePresent {
continue
}
if !dpNameDER.ReadASN1(&dpNameDER, cbasn1.Tag(0).Constructed().ContextSpecific()) {
return nil, errors.New("x509: invalid CRL distribution point")
}
for !dpNameDER.Empty() {
if !dpNameDER.PeekASN1Tag(cbasn1.Tag(6).ContextSpecific()) {
break
}
var uri cryptobyte.String
if !dpNameDER.ReadASN1(&uri, cbasn1.Tag(6).ContextSpecific()) {
return nil, errors.New("x509: invalid CRL distribution point")
}

Check warning on line 225 in revocation/crl/fetcher.go

View check run for this annotation

Codecov / codecov/patch

revocation/crl/fetcher.go#L224-L225

Added lines #L224 - L225 were not covered by tests
urls = append(urls, string(uri))
}
}
return urls, nil
}

func fetchCRL(ctx context.Context, crlURL string, client *http.Client) (*x509.RevocationList, error) {
// validate URL
parsedURL, err := url.Parse(crlURL)
Expand Down
Loading
Loading