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 2 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
25 changes: 12 additions & 13 deletions revocation/crl/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@
"io"
"net/http"
"net/url"
"slices"
"time"

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

// oidFreshestCRL is the object identifier for the distribution point
Expand Down Expand Up @@ -142,17 +142,16 @@
//
// It returns errDeltaCRLNotFound if the delta CRL is not found.
func (f *HTTPFetcher) fetchDeltaCRL(ctx context.Context, extensions []pkix.Extension) (*x509.RevocationList, error) {
idx := slices.IndexFunc(extensions, func(ext pkix.Extension) bool {
return ext.Id.Equal(oidFreshestCRL)
})
if idx < 0 {
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(extensions[idx].Value)
urls, err := parseCRLDistributionPoint(extension.Value)
if err != nil {
return nil, fmt.Errorf("failed to parse Freshest CRL extension: %w", err)
}
Expand Down Expand Up @@ -197,33 +196,33 @@
// fullName [0] GeneralNames,
// nameRelativeToCRLIssuer [1] RelativeDistinguishedName }
val := cryptobyte.String(value)
if !val.ReadASN1(&val, cryptobyte_asn1.SEQUENCE) {
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, cryptobyte_asn1.SEQUENCE) {
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, cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) {
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, cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) {
if !dpNameDER.ReadASN1(&dpNameDER, cbasn1.Tag(0).Constructed().ContextSpecific()) {
return nil, errors.New("x509: invalid CRL distribution point")
}
for !dpNameDER.Empty() {
if !dpNameDER.PeekASN1Tag(cryptobyte_asn1.Tag(6).ContextSpecific()) {
if !dpNameDER.PeekASN1Tag(cbasn1.Tag(6).ContextSpecific()) {
break
}
var uri cryptobyte.String
if !dpNameDER.ReadASN1(&uri, cryptobyte_asn1.Tag(6).ContextSpecific()) {
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))
}
}
Expand Down
46 changes: 23 additions & 23 deletions revocation/internal/crl/crl.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,34 @@ import (
"errors"
"fmt"
"math/big"
"slices"
"time"

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

// RFC 5280, 5.3.1
//
// CRLReason ::= ENUMERATED {
// unspecified (0),
// keyCompromise (1),
// cACompromise (2),
// affiliationChanged (3),
// superseded (4),
// cessationOfOperation (5),
// certificateHold (6),
// -- value 7 is not used
// removeFromCRL (8),
// privilegeWithdrawn (9),
// aACompromise (10) }
const (
// RFC 5280, 5.3.1
// CRLReason ::= ENUMERATED {
// unspecified (0),
// keyCompromise (1),
// cACompromise (2),
// affiliationChanged (3),
// superseded (4),
// cessationOfOperation (5),
// certificateHold (6),
// -- value 7 is not used
// removeFromCRL (8),
// privilegeWithdrawn (9),
// aACompromise (10) }
// certificateHold
reasonCodeCertificateHold = 6
reasonCodeRemoveFromCRL = 8

// removeFromCRL
reasonCodeRemoveFromCRL = 8
)
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved

var (
Expand Down Expand Up @@ -197,9 +201,7 @@ func Supported(cert *x509.Certificate) bool {
}

func hasFreshestCRL(extensions []pkix.Extension) bool {
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
return slices.ContainsFunc(extensions, func(ext pkix.Extension) bool {
return ext.Id.Equal(oidFreshestCRL)
})
return x509util.FindExtensionByOID(oidFreshestCRL, extensions) != nil
}

func validate(bundle *crl.Bundle, issuer *x509.Certificate) error {
Expand All @@ -224,14 +226,12 @@ func validate(bundle *crl.Bundle, issuer *x509.Certificate) error {
}

// check delta CRL indicator extension
idx := slices.IndexFunc(deltaCRL.Extensions, func(ext pkix.Extension) bool {
return ext.Id.Equal(oidDeltaCRLIndicator)
})
if idx < 0 {
extension := x509util.FindExtensionByOID(oidDeltaCRLIndicator, deltaCRL.Extensions)
if extension == nil {
return errors.New("delta CRL indicator extension is not found")
}
minimumBaseCRLNumber := new(big.Int)
value := cryptobyte.String(deltaCRL.Extensions[idx].Value)
value := cryptobyte.String(extension.Value)
if !value.ReadASN1Integer(minimumBaseCRLNumber) {
return errors.New("failed to parse delta CRL indicator extension")
}
Expand Down
31 changes: 31 additions & 0 deletions revocation/internal/x509util/extension.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// 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 x509util

import (
"crypto/x509/pkix"
"encoding/asn1"
"slices"
)

// FindExtensionByOID finds the extension by the given OID.
func FindExtensionByOID(oid asn1.ObjectIdentifier, extensions []pkix.Extension) *pkix.Extension {
JeyJeyGao marked this conversation as resolved.
Show resolved Hide resolved
idx := slices.IndexFunc(extensions, func(ext pkix.Extension) bool {
return ext.Id.Equal(oid)
})
if idx < 0 {
return nil
}
return &extensions[idx]
}
58 changes: 58 additions & 0 deletions revocation/internal/x509util/extension_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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 x509util

import (
"crypto/x509/pkix"
"encoding/asn1"
"testing"
)

func TestFindExtensionByOID(t *testing.T) {
oid1 := asn1.ObjectIdentifier{1, 2, 3, 4}
oid2 := asn1.ObjectIdentifier{1, 2, 3, 5}
extensions := []pkix.Extension{
{Id: oid1, Value: []byte("value1")},
{Id: oid2, Value: []byte("value2")},
}

tests := []struct {
name string
oid asn1.ObjectIdentifier
extensions []pkix.Extension
expected *pkix.Extension
}{
{
name: "Extension found",
oid: oid1,
extensions: extensions,
expected: &extensions[0],
},
{
name: "Extension not found",
oid: asn1.ObjectIdentifier{1, 2, 3, 6},
extensions: extensions,
expected: nil,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := FindExtensionByOID(tt.oid, tt.extensions)
if result != tt.expected {
t.Errorf("expected %v, got %v", tt.expected, result)
}
})
}
}
3 changes: 2 additions & 1 deletion revocation/internal/x509util/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
// limitations under the License.

// Package x509util provides the method to validate the certificate chain for a
// specific purpose, including code signing and timestamping.
// specific purpose, including code signing and timestamping. It also provides
// the method to find the extension by the given OID.
package x509util

import (
Expand Down
Loading