Skip to content

Commit

Permalink
feat: [OIDC4VP] Verifier interaction with Trust registry (#1539)
Browse files Browse the repository at this point in the history
Signed-off-by: Mykhailo Sizov <[email protected]>
  • Loading branch information
mishasizov-SK authored Nov 29, 2023
1 parent 3b5ec5b commit 05e86f7
Show file tree
Hide file tree
Showing 27 changed files with 1,307 additions and 385 deletions.
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

0 comments on commit 05e86f7

Please sign in to comment.