Skip to content

Commit

Permalink
pass pki.Validator to resolve validator
Browse files Browse the repository at this point in the history
  • Loading branch information
woutslakhorst committed Dec 20, 2024
1 parent 416df20 commit bcde56c
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 24 deletions.
2 changes: 1 addition & 1 deletion auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ func (auth *Auth) Configure(config core.ServerConfig) error {
auth.authzServer = oauth.NewAuthorizationServer(auth.vdrInstance.Resolver(), auth.vcr, auth.vcr.Verifier(), auth.serviceResolver,
auth.keyStore, auth.contractNotary, auth.jsonldManager, accessTokenLifeSpan)
auth.relyingParty = oauth.NewRelyingParty(auth.vdrInstance.Resolver(), auth.serviceResolver,
auth.keyStore, auth.vcr.Wallet(), auth.httpClientTimeout, auth.tlsConfig, config.Strictmode)
auth.keyStore, auth.vcr.Wallet(), auth.httpClientTimeout, auth.tlsConfig, config.Strictmode, auth.pkiProvider)

if err := auth.authzServer.Configure(auth.config.ClockSkew, config.Strictmode); err != nil {
return err
Expand Down
7 changes: 5 additions & 2 deletions auth/services/oauth/relying_party.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"context"
"crypto/tls"
"fmt"
"github.com/nuts-foundation/nuts-node/pki"
"net/url"
"strings"
"time"
Expand Down Expand Up @@ -50,12 +51,13 @@ type relyingParty struct {
httpClientTimeout time.Duration
httpClientTLS *tls.Config
wallet holder.Wallet
pkiValidator pki.Validator
}

// NewRelyingParty returns an implementation of RelyingParty
func NewRelyingParty(
didResolver resolver.DIDResolver, serviceResolver didman.CompoundServiceResolver, privateKeyStore nutsCrypto.KeyStore,
wallet holder.Wallet, httpClientTimeout time.Duration, httpClientTLS *tls.Config, strictMode bool) RelyingParty {
wallet holder.Wallet, httpClientTimeout time.Duration, httpClientTLS *tls.Config, strictMode bool, pkiValidator pki.Validator) RelyingParty {
return &relyingParty{
keyResolver: resolver.DIDKeyResolver{Resolver: didResolver},
serviceResolver: serviceResolver,
Expand All @@ -64,6 +66,7 @@ func NewRelyingParty(
httpClientTLS: httpClientTLS,
strictMode: strictMode,
wallet: wallet,
pkiValidator: pkiValidator,
}
}

Expand All @@ -81,7 +84,7 @@ func (s *relyingParty) CreateJwtGrant(ctx context.Context, request services.Crea
}

for _, verifiableCredential := range request.Credentials {
validator := credential.FindValidator(verifiableCredential)
validator := credential.FindValidator(verifiableCredential, s.pkiValidator)
if err := validator.Validate(verifiableCredential); err != nil {
return nil, fmt.Errorf("invalid VerifiableCredential: %w", err)
}
Expand Down
5 changes: 3 additions & 2 deletions vcr/credential/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ import (
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/crypto"
"github.com/nuts-foundation/nuts-node/pki"
"github.com/nuts-foundation/nuts-node/vcr/signature/proof"
)

// FindValidator finds the Validator the provided credential based on its Type
// When no additional type is provided, it returns the default validator
func FindValidator(credential vc.VerifiableCredential) Validator {
func FindValidator(credential vc.VerifiableCredential, pkiValidator pki.Validator) Validator {
if vcTypes := ExtractTypes(credential); len(vcTypes) > 0 {
for _, t := range vcTypes {
switch t {
Expand All @@ -39,7 +40,7 @@ func FindValidator(credential vc.VerifiableCredential) Validator {
case NutsAuthorizationCredentialType:
return nutsAuthorizationCredentialValidator{}
case X509CredentialType:
return x509CredentialValidator{}
return x509CredentialValidator{pkiValidator: pkiValidator}
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions vcr/credential/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,19 @@ import (

func TestFindValidator(t *testing.T) {
t.Run("an unknown type returns the default validator", func(t *testing.T) {
assert.IsType(t, defaultCredentialValidator{}, FindValidator(vc.VerifiableCredential{}))
assert.IsType(t, defaultCredentialValidator{}, FindValidator(vc.VerifiableCredential{}, nil))
})

t.Run("validator found for NutsOrganizationCredential", func(t *testing.T) {
assert.IsType(t, nutsOrganizationCredentialValidator{}, FindValidator(test.ValidNutsOrganizationCredential(t)))
assert.IsType(t, nutsOrganizationCredentialValidator{}, FindValidator(test.ValidNutsOrganizationCredential(t), nil))
})

t.Run("validator found for NutsAuthorizationCredential", func(t *testing.T) {
assert.IsType(t, nutsAuthorizationCredentialValidator{}, FindValidator(test.ValidNutsAuthorizationCredential(t)))
assert.IsType(t, nutsAuthorizationCredentialValidator{}, FindValidator(test.ValidNutsAuthorizationCredential(t), nil))
})

t.Run("validator found for X509Credential", func(t *testing.T) {
assert.IsType(t, x509CredentialValidator{}, FindValidator(test.ValidX509Credential(t)))
assert.IsType(t, x509CredentialValidator{}, FindValidator(test.ValidX509Credential(t), nil))
})
}

Expand Down
33 changes: 28 additions & 5 deletions vcr/credential/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,21 @@
package credential

import (
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/nuts-foundation/nuts-node/crypto"
"github.com/nuts-foundation/nuts-node/vdr/didx509"
"net/url"
"strings"

"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/nuts-foundation/go-did/did"
"github.com/nuts-foundation/go-did/vc"
"github.com/nuts-foundation/nuts-node/crypto"
"github.com/nuts-foundation/nuts-node/pki"
"github.com/nuts-foundation/nuts-node/vcr/revocation"
"github.com/nuts-foundation/nuts-node/vdr/didx509"
"github.com/nuts-foundation/nuts-node/vdr/resolver"
"net/url"
"strings"
)

// Validator is the interface specific VC verification.
Expand Down Expand Up @@ -259,6 +262,7 @@ func validateNutsCredentialID(credential vc.VerifiableCredential) error {

// x509CredentialValidator checks the did:x509 issuer and if the credentialSubject claims match the x509 certificate
type x509CredentialValidator struct {
pkiValidator pki.Validator
}

func (d x509CredentialValidator) Validate(credential vc.VerifiableCredential) error {
Expand Down Expand Up @@ -287,6 +291,25 @@ func (d x509CredentialValidator) Validate(credential vc.VerifiableCredential) er
return fmt.Errorf("%w: %w", errValidation, err)
}

chainHeader, _ := resolveMetadata.GetProtectedHeaderChain(jwk.X509CertChainKey) // already succeeded for resolve
// convert cert.Chain to []*x509.Certificate
chain := make([]*x509.Certificate, chainHeader.Len())
for i := 0; i < chainHeader.Len(); i++ {
base64Cert, _ := chainHeader.Get(i)
der, err := base64.StdEncoding.DecodeString(string(base64Cert))
if err != nil {
return fmt.Errorf("%w: invalid certificate chain: %w", errValidation, err)
}
cert, err := x509.ParseCertificate(der)
if err != nil {
return fmt.Errorf("%w: invalid certificate chain: %w", errValidation, err)
}
chain[i] = cert
}
if err = d.pkiValidator.CheckCRL(chain); err != nil {
return fmt.Errorf("%w: %w", errValidation, err)
}

return (defaultCredentialValidator{}).Validate(credential)
}

Expand Down
38 changes: 34 additions & 4 deletions vcr/credential/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
package credential

import (
"github.com/nuts-foundation/nuts-node/pki"
"go.uber.org/mock/gomock"
"testing"
"time"

Expand Down Expand Up @@ -498,19 +500,31 @@ func Test_validateCredentialStatus(t *testing.T) {
}

func TestX509CredentialValidator_Validate(t *testing.T) {
validator := x509CredentialValidator{}
ctx := createTestContext(t)

t.Run("ok", func(t *testing.T) {
x509credential := test.ValidX509Credential(t)
ctx := createTestContext(t)
ctx.pkiValidator.EXPECT().CheckCRL(gomock.Any()).Return(nil)

err := validator.Validate(x509credential)
err := ctx.validator.Validate(x509credential)

assert.NoError(t, err)
})
t.Run("CRL check failed", func(t *testing.T) {
x509credential := test.ValidX509Credential(t)
ctx := createTestContext(t)
ctx.pkiValidator.EXPECT().CheckCRL(gomock.Any()).Return(assert.AnError)

err := ctx.validator.Validate(x509credential)

assert.ErrorIs(t, err, errValidation)
assert.ErrorIs(t, err, assert.AnError)
})
t.Run("invalid did", func(t *testing.T) {
x509credential := vc.VerifiableCredential{Issuer: ssi.MustParseURI("not_a_did")}

err := validator.Validate(x509credential)
err := ctx.validator.Validate(x509credential)

assert.ErrorIs(t, err, errValidation)
assert.ErrorIs(t, err, did.ErrInvalidDID)
Expand Down Expand Up @@ -555,11 +569,27 @@ func TestX509CredentialValidator_Validate(t *testing.T) {
return builder
})

err := validator.Validate(x509credential)
err := ctx.validator.Validate(x509credential)

assert.ErrorIs(t, err, errValidation)
assert.ErrorContains(t, err, tc.expectedError)
})
}
})
}

type testContext struct {
ctrl *gomock.Controller
validator x509CredentialValidator
pkiValidator *pki.MockValidator
}

func createTestContext(t *testing.T) testContext {
ctrl := gomock.NewController(t)
pkiValidator := pki.NewMockValidator(ctrl)
return testContext{
ctrl: ctrl,
validator: x509CredentialValidator{pkiValidator: pkiValidator},
pkiValidator: pkiValidator,
}
}
3 changes: 2 additions & 1 deletion vcr/issuer/issuer.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ func (i issuer) Issue(ctx context.Context, template vc.VerifiableCredential, opt
}

// Validate the VC using the type-specific validator
validator := credential.FindValidator(*createdVC)
// we don't pass a pki.Validator since we don't issue x509 certs
validator := credential.FindValidator(*createdVC, nil)
if err := validator.Validate(*createdVC); err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion vcr/issuer/issuer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ func TestIssuer_StatusList(t *testing.T) {
vDIDResolverMock.EXPECT().Resolve(gomock.Any(), gomock.Any())
vKeyResolverMock := resolver.NewMockKeyResolver(ctrl)
vKeyResolverMock.EXPECT().ResolveKeyByID(gomock.Any(), gomock.Any(), gomock.Any()).Return(signingKey, nil)
verif := verifier.NewVerifier(vStoreMock, vDIDResolverMock, vKeyResolverMock, jsonldManager, nil, &revocation.StatusList2021{})
verif := verifier.NewVerifier(vStoreMock, vDIDResolverMock, vKeyResolverMock, jsonldManager, nil, &revocation.StatusList2021{}, nil)
assert.NoError(t, verif.Verify(*result, true, true, nil))
})
t.Run("error - unknown status list credential", func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion vcr/vcr.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func (c *vcr) Configure(config core.ServerConfig) error {

status := revocation.NewStatusList2021(c.storageClient.GetSQLDatabase(), client.NewWithCache(config.HTTPClient.Timeout), config.URL)
c.issuer = issuer.NewIssuer(c.issuerStore, c, networkPublisher, openidHandlerFn, didResolver, c.keyStore, c.jsonldManager, c.trustConfig, status)
c.verifier = verifier.NewVerifier(c.verifierStore, didResolver, c.keyResolver, c.jsonldManager, c.trustConfig, status)
c.verifier = verifier.NewVerifier(c.verifierStore, didResolver, c.keyResolver, c.jsonldManager, c.trustConfig, status, c.pkiProvider)

if !c.network.Disabled() {
c.ambassador = NewAmbassador(c.network, c, c.verifier, c.eventManager)
Expand Down
7 changes: 5 additions & 2 deletions vcr/verifier/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/nuts-foundation/nuts-node/pki"
"github.com/nuts-foundation/nuts-node/vcr/revocation"
"strings"
"time"
Expand Down Expand Up @@ -56,6 +57,7 @@ type verifier struct {
trustConfig *trust.Config
signatureVerifier
credentialStatus revocation.StatusList2021Verifier
pkiValidator pki.Validator
}

// VerificationError is used to describe a VC/VP verification failure.
Expand Down Expand Up @@ -83,13 +85,14 @@ func (e VerificationError) Error() string {
}

// NewVerifier creates a new instance of the verifier. It needs a key resolver for validating signatures.
func NewVerifier(store Store, didResolver resolver.DIDResolver, keyResolver resolver.KeyResolver, jsonldManager jsonld.JSONLD, trustConfig *trust.Config, credentialStatus *revocation.StatusList2021) Verifier {
func NewVerifier(store Store, didResolver resolver.DIDResolver, keyResolver resolver.KeyResolver, jsonldManager jsonld.JSONLD, trustConfig *trust.Config, credentialStatus *revocation.StatusList2021, pkiValidator pki.Validator) Verifier {
v := &verifier{store: store, didResolver: didResolver, keyResolver: keyResolver, jsonldManager: jsonldManager, trustConfig: trustConfig,
signatureVerifier: signatureVerifier{
keyResolver: keyResolver,
jsonldManager: jsonldManager,
},
credentialStatus: credentialStatus,
pkiValidator: pkiValidator,
}
credentialStatus.VerifySignature = v.VerifySignature
return v
Expand All @@ -99,7 +102,7 @@ func NewVerifier(store Store, didResolver resolver.DIDResolver, keyResolver reso
// It currently checks if the credential has the required fields and values, if it is valid at the given time and optional the signature.
func (v verifier) Verify(credentialToVerify vc.VerifiableCredential, allowUntrusted bool, checkSignature bool, validAt *time.Time) error {
// it must have valid content
validator := credential.FindValidator(credentialToVerify)
validator := credential.FindValidator(credentialToVerify, v.pkiValidator)
if err := validator.Validate(credentialToVerify); err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion vcr/verifier/verifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ func newMockContext(t *testing.T) mockContext {
verifierStore := NewMockStore(ctrl)
trustConfig := trust.NewConfig(path.Join(io.TestDirectory(t), "trust.yaml"))
db := orm.NewTestDatabase(t)
verifier := NewVerifier(verifierStore, didResolver, keyResolver, jsonldManager, trustConfig, revocation.NewStatusList2021(db, nil, "")).(*verifier)
verifier := NewVerifier(verifierStore, didResolver, keyResolver, jsonldManager, trustConfig, revocation.NewStatusList2021(db, nil, ""), nil).(*verifier)
return mockContext{
ctrl: ctrl,
verifier: verifier,
Expand Down

0 comments on commit bcde56c

Please sign in to comment.