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: [OIDC4VP] Verifier interaction with Trust registry #1539

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions cmd/vc-rest/startcmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -862,9 +862,10 @@ func buildEchoHandler(
var verifyPresentationSvc verifypresentation.ServiceInterface

verifyPresentationSvc = verifypresentation.New(&verifypresentation.Config{
VcVerifier: verifyCredentialSvc,
DocumentLoader: documentLoader,
VDR: conf.VDR,
VcVerifier: verifyCredentialSvc,
DocumentLoader: documentLoader,
VDR: conf.VDR,
ClientAttestationService: clientAttestationService,
})

if conf.IsTraceEnabled {
Expand Down
7 changes: 3 additions & 4 deletions component/wallet-cli/pkg/oidc4vp/oidc4vp_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ import (
"github.com/trustbloc/vc-go/vermethod"

jwssigner "github.com/trustbloc/vcs/component/wallet-cli/pkg/signer"
"github.com/trustbloc/vcs/component/wallet-cli/pkg/trustregistry"
"github.com/trustbloc/vcs/component/wallet-cli/pkg/wallet"
"github.com/trustbloc/vcs/pkg/doc/vc"
vccrypto "github.com/trustbloc/vcs/pkg/doc/vc/crypto"
vcs "github.com/trustbloc/vcs/pkg/doc/verifiable"
vcskms "github.com/trustbloc/vcs/pkg/kms"
kmssigner "github.com/trustbloc/vcs/pkg/kms/signer"
"github.com/trustbloc/vcs/pkg/observability/metrics/noop"
"github.com/trustbloc/vcs/pkg/service/trustregistry"
)

const (
Expand Down Expand Up @@ -151,11 +151,10 @@ func (f *Flow) Run(ctx context.Context) error {
slog.Info("Run Trust Registry Verifier validation", "url", trustRegistryURL)

trustRegistry := trustregistry.New(&trustregistry.Config{
TrustRegistryURL: trustRegistryURL,
HTTPClient: f.httpClient,
HTTPClient: f.httpClient,
})

if err = trustRegistry.ValidateVerifier(requestObject.ClientID, credentials); err != nil {
if err = trustRegistry.ValidateVerifier(trustRegistryURL, requestObject.ClientID, credentials); err != nil {
return fmt.Errorf("trust registry verifier validation: %w", err)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,6 @@ type VerifierValidationConfig struct {
Metadata []*CredentialMetadata `json:"metadata"`
}

type PresentationValidationConfig struct {
PolicyID string `json:"policy_id"`
AttestationVC interface{} `json:"attestation_vc"`
Metadata []*CredentialMetadata `json:"metadata"`
}

type CredentialMetadata struct {
CredentialID string `json:"credential_id"`
Types []string `json:"types"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@ package trustregistry

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/http"

"github.com/samber/lo"

"github.com/trustbloc/logutil-go/pkg/log"
"github.com/trustbloc/vc-go/verifiable"
)
Expand All @@ -24,28 +23,21 @@ var (
logger = log.New("trustregistry")
)

const (
walletAttestationVCType = "WalletAttestationCredential"
)

type Config struct {
HTTPClient *http.Client
}
type Service struct {
url string
httpClient *http.Client
}

type Config struct {
TrustRegistryURL string
HTTPClient *http.Client
}

func New(conf *Config) *Service {
return &Service{
url: conf.TrustRegistryURL,
httpClient: conf.HTTPClient,
}
return &Service{httpClient: conf.HTTPClient}
}

func (s *Service) ValidateVerifier(verifierDID string, presentationCredentials []*verifiable.Credential) error {
func (s *Service) ValidateVerifier(
policyURL, verifierDID string,
presentationCredentials []*verifiable.Credential,
) error {
logger.Debug("ValidateVerifier begin")
verifierValidationConfig := &VerifierValidationConfig{
VerifierDID: verifierDID,
Expand All @@ -55,15 +47,15 @@ func (s *Service) ValidateVerifier(verifierDID string, presentationCredentials [
for i, credential := range presentationCredentials {
content := credential.Contents()

verifierValidationConfig.Metadata[i] = s.getCredentialMetadata(content)
verifierValidationConfig.Metadata[i] = getTrustRegistryCredentialMetadata(content)
}

req, err := json.Marshal(verifierValidationConfig)
reqPayload, err := json.Marshal(verifierValidationConfig)
if err != nil {
return fmt.Errorf("encode verifier config: %w", err)
}

responseDecoded, err := s.doRequest(req)
responseDecoded, err := s.doTrustRegistryRequest(context.Background(), policyURL, reqPayload)
if err != nil {
return err
}
Expand All @@ -77,48 +69,15 @@ func (s *Service) ValidateVerifier(verifierDID string, presentationCredentials [
return nil
}

func (s *Service) ValidatePresentation(policyID string, presentationCredentials []*verifiable.Credential) error {
logger.Debug("ValidatePresentation begin")

presentationValidationConfig := &PresentationValidationConfig{
PolicyID: policyID,
Metadata: make([]*CredentialMetadata, len(presentationCredentials)),
}

for i, credential := range presentationCredentials {
content := credential.Contents()

if lo.Contains(content.Types, walletAttestationVCType) {
attestationVC, err := credential.ToUniversalForm()
if err == nil {
presentationValidationConfig.AttestationVC = attestationVC
}
}

presentationValidationConfig.Metadata[i] = s.getCredentialMetadata(content)
}

req, err := json.Marshal(presentationValidationConfig)
func (s *Service) doTrustRegistryRequest(ctx context.Context, policyURL string, req []byte) (*Response, error) {
request, err := http.NewRequestWithContext(ctx, http.MethodPost, policyURL, bytes.NewReader(req))
if err != nil {
return fmt.Errorf("encode presentation config: %w", err)
}

responseDecoded, err := s.doRequest(req)
if err != nil {
return err
return nil, fmt.Errorf("create request: %w", err)
}

if !responseDecoded.Allowed {
return ErrInteractionRestricted
}

logger.Debug("ValidatePresentation succeed")

return nil
}
request.Header.Add("content-type", "application/json")

func (s *Service) doRequest(req []byte) (*Response, error) {
resp, err := s.httpClient.Post(s.url, "application/json", bytes.NewReader(req)) //nolint:noctx
resp, err := s.httpClient.Do(request)
if err != nil {
return nil, fmt.Errorf("send request: %w", err)
}
Expand All @@ -138,7 +97,7 @@ func (s *Service) doRequest(req []byte) (*Response, error) {
return responseDecoded, nil
}

func (s *Service) getCredentialMetadata(content verifiable.CredentialContents) *CredentialMetadata {
func getTrustRegistryCredentialMetadata(content verifiable.CredentialContents) *CredentialMetadata {
var iss, exp string
if content.Issued != nil {
iss = content.Issued.FormatToString()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package trustregistry

import (
Expand All @@ -14,11 +8,14 @@ import (

"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"

util "github.com/trustbloc/did-go/doc/util/time"
"github.com/trustbloc/vc-go/verifiable"
)

const (
walletAttestationVCType = "WalletAttestationCredential"
)

func TestService_ValidateVerifier(t *testing.T) {
now := time.Now()
handler := echo.New()
Expand Down Expand Up @@ -137,115 +134,10 @@ func TestService_ValidateVerifier(t *testing.T) {
tt.addTestCaseHandler(t, handler)

s := &Service{
url: tt.fields.url,
httpClient: http.DefaultClient,
}

err := s.ValidateVerifier(tt.args.verifierDID, tt.args.getPresentationCredentials(t, now))
if (err != nil) != tt.wantErr {
t.Errorf("ValidateVerifier() error = %v, wantErr %v", err, tt.wantErr)
}

if tt.wantErr {
assert.ErrorContains(t, err, tt.errContains)
}
})
}
}

func TestService_ValidatePresentation(t *testing.T) {
now := time.Now()
handler := echo.New()

srv := httptest.NewServer(handler)
defer srv.Close()

type fields struct {
url string
}
type args struct {
policyID string
getPresentationCredentials func(t *testing.T, now time.Time) []*verifiable.Credential
}
tests := []struct {
name string
addTestCaseHandler func(t *testing.T, e *echo.Echo)
fields fields
args args
wantErr bool
errContains string
}{
{
name: "Success",
addTestCaseHandler: func(t *testing.T, e *echo.Echo) {
e.Add(http.MethodPost, "/testcase1", func(c echo.Context) error {
var got *PresentationValidationConfig
assert.NoError(t, c.Bind(&got))

attestationVCUniversalForm, err := getAttestationCredential(t, now).ToUniversalForm()
assert.NoError(t, err)

expected := &PresentationValidationConfig{
PolicyID: "policy1",
AttestationVC: attestationVCUniversalForm,
Metadata: getDefaultMetadata(t, now),
}

assert.Equal(t, expected, got)

return c.JSON(http.StatusOK, map[string]bool{"allowed": true})
})
},
fields: fields{
url: srv.URL + "/testcase1",
},
args: args{
policyID: "policy1",
getPresentationCredentials: getDefaultCredentials,
},
wantErr: false,
},
{
name: "httpClient.Post error",
addTestCaseHandler: func(t *testing.T, e *echo.Echo) {},
fields: fields{
url: "abcd",
},
args: args{
policyID: "policy1",
getPresentationCredentials: getDefaultCredentials,
},
wantErr: true,
errContains: "send request:",
},
{
name: "Interaction restricted error",
addTestCaseHandler: func(t *testing.T, e *echo.Echo) {
e.Add(http.MethodPost, "/testcase2", func(c echo.Context) error {
return c.JSON(http.StatusOK, map[string]bool{"allowed": false})
})
},
fields: fields{
url: srv.URL + "/testcase2",
},
args: args{
policyID: "policy1",
getPresentationCredentials: getDefaultCredentials,
},
wantErr: true,
errContains: "interaction restricted",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tt.addTestCaseHandler(t, handler)

s := New(&Config{
TrustRegistryURL: tt.fields.url,
HTTPClient: http.DefaultClient,
})

err := s.ValidatePresentation(tt.args.policyID, tt.args.getPresentationCredentials(t, now))
err := s.ValidateVerifier(tt.fields.url, tt.args.verifierDID, tt.args.getPresentationCredentials(t, now))
if (err != nil) != tt.wantErr {
t.Errorf("ValidateVerifier() error = %v, wantErr %v", err, tt.wantErr)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ import (
"github.com/trustbloc/vc-go/verifiable"

"github.com/trustbloc/vcs/component/wallet-cli/internal/httputil"
"github.com/trustbloc/vcs/component/wallet-cli/pkg/trustregistry"
"github.com/trustbloc/vcs/pkg/doc/vc"
vccrypto "github.com/trustbloc/vcs/pkg/doc/vc/crypto"
vcs "github.com/trustbloc/vcs/pkg/doc/verifiable"
vcskms "github.com/trustbloc/vcs/pkg/kms"
"github.com/trustbloc/vcs/pkg/kms/signer"
"github.com/trustbloc/vcs/pkg/observability/metrics/noop"
"github.com/trustbloc/vcs/pkg/service/trustregistry"
)

type RPConfigOverride func(rpc *RPConfig)
Expand Down Expand Up @@ -127,13 +127,12 @@ func (s *Service) RunOIDC4VPFlow(ctx context.Context, authorizationRequest strin
log.Println("Run Trust Registry Verifier validation")

trustRegistry := trustregistry.New(&trustregistry.Config{
TrustRegistryURL: trustRegistryURL,
HTTPClient: s.httpClient,
HTTPClient: s.httpClient,
})

credentials := s.vpFlowExecutor.requestPresentation[0].Credentials()

if err = trustRegistry.ValidateVerifier(s.vpFlowExecutor.requestObject.ClientID, credentials); err != nil {
if err = trustRegistry.ValidateVerifier(trustRegistryURL, s.vpFlowExecutor.requestObject.ClientID, credentials); err != nil {
return fmt.Errorf("trust registry verifier validation: %w", err)
}
}
Expand Down
12 changes: 12 additions & 0 deletions pkg/internal/testutil/contexts/wallet_attestation_vc_v1.jsonld
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"@context": {
"@version": 1.1,
"id": "@id",
"type": "@type",
"WalletAttestationCredential": "ex:WalletAttestationCredential",

"key_type": "ex:key_type",
"user_authentication": "ex:user_authentication",
"assurance_level": "ex:assurance_level"
}
}
6 changes: 6 additions & 0 deletions pkg/internal/testutil/document_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ var (
vcStatusList2021 []byte
//go:embed contexts/vc-data-integrity-v1.jsonld
vcDataIntegrity []byte
//go:embed contexts/wallet_attestation_vc_v1.jsonld
walletAttestationVC []byte
)

type mockLDStoreProvider struct {
Expand Down Expand Up @@ -87,6 +89,10 @@ func DocumentLoader(t *testing.T, extraContexts ...ldcontext.Document) *ld.Docum
URL: "https://w3id.org/security/data-integrity/v1",
Content: vcDataIntegrity,
},
ldcontext.Document{
URL: "https://www.w3.org/2022/credentials/walletAttestation/v1",
Content: walletAttestationVC,
},
}

loader, err := ld.NewDocumentLoader(ldStore,
Expand Down
Loading