Skip to content

Commit

Permalink
feat: [OIDC4VP] Update OIDC4VP flow to support updated Trust Registry…
Browse files Browse the repository at this point in the history
… spec (#1541)
  • Loading branch information
mishasizov-SK authored Nov 30, 2023
1 parent 05e86f7 commit 95cdf61
Show file tree
Hide file tree
Showing 19 changed files with 247 additions and 101 deletions.
2 changes: 1 addition & 1 deletion component/wallet-cli/pkg/oidc4vp/oidc4vp_flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func (f *Flow) Run(ctx context.Context) error {
HTTPClient: f.httpClient,
})

if err = trustRegistry.ValidateVerifier(trustRegistryURL, requestObject.ClientID, credentials); err != nil {
if err = trustRegistry.ValidateWalletPresentation(trustRegistryURL, requestObject.ClientID, credentials); err != nil {
return fmt.Errorf("trust registry verifier validation: %w", err)
}
}
Expand Down
24 changes: 17 additions & 7 deletions component/wallet-cli/pkg/trustregistry/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,27 @@ SPDX-License-Identifier: Apache-2.0

package trustregistry

type VerifierValidationConfig struct {
type WalletPresentationValidationConfig struct {
VerifierDID string `json:"verifier_did"`
Metadata []*CredentialMetadata `json:"metadata"`
Metadata []*CredentialMetadata `json:"credential_metadata"`
}

// CredentialMetadata defines model for CredentialMetadata.
type CredentialMetadata struct {
CredentialID string `json:"credential_id"`
Types []string `json:"types"`
Issuer string `json:"issuer"`
Issued string `json:"issued"`
Expired string `json:"expired"`
// Credential ID
CredentialID string `json:"credential_id,omitempty"`

// Credential Types
CredentialTypes []string `json:"credential_types,omitempty"`

// Expiration date/time.
ExpirationDate string `json:"expiration_date,omitempty"`

// Issuance date/time.
IssuanceDate string `json:"issuance_date,omitempty"`

// Issuer ID
IssuerID string `json:"issuer_id,omitempty"`
}

type Response struct {
Expand Down
23 changes: 12 additions & 11 deletions component/wallet-cli/pkg/trustregistry/trustregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,23 +34,24 @@ func New(conf *Config) *Service {
return &Service{httpClient: conf.HTTPClient}
}

func (s *Service) ValidateVerifier(
// ValidateWalletPresentation validates wallet presentation using Trust Registry API.
func (s *Service) ValidateWalletPresentation(
policyURL, verifierDID string,
presentationCredentials []*verifiable.Credential,
) error {
logger.Debug("ValidateVerifier begin")
verifierValidationConfig := &VerifierValidationConfig{
logger.Debug("ValidateWalletPresentation begin")
walletPresentationConfig := &WalletPresentationValidationConfig{
VerifierDID: verifierDID,
Metadata: make([]*CredentialMetadata, len(presentationCredentials)),
}

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

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

reqPayload, err := json.Marshal(verifierValidationConfig)
reqPayload, err := json.Marshal(walletPresentationConfig)
if err != nil {
return fmt.Errorf("encode verifier config: %w", err)
}
Expand All @@ -64,7 +65,7 @@ func (s *Service) ValidateVerifier(
return ErrInteractionRestricted
}

logger.Debug("ValidateVerifier succeed")
logger.Debug("ValidateWalletPresentation succeed")

return nil
}
Expand Down Expand Up @@ -108,10 +109,10 @@ func getTrustRegistryCredentialMetadata(content verifiable.CredentialContents) *
}

return &CredentialMetadata{
CredentialID: content.ID,
Types: content.Types,
Issuer: content.Issuer.ID,
Issued: iss,
Expired: exp,
CredentialID: content.ID,
CredentialTypes: content.Types,
ExpirationDate: exp,
IssuanceDate: iss,
IssuerID: content.Issuer.ID,
}
}
26 changes: 13 additions & 13 deletions component/wallet-cli/pkg/trustregistry/trustregistry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const (
walletAttestationVCType = "WalletAttestationCredential"
)

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

Expand All @@ -42,10 +42,10 @@ func TestService_ValidateVerifier(t *testing.T) {
name: "Success",
addTestCaseHandler: func(t *testing.T, e *echo.Echo) {
e.Add(http.MethodPost, "/testcase1", func(c echo.Context) error {
var got *VerifierValidationConfig
var got *WalletPresentationValidationConfig
assert.NoError(t, c.Bind(&got))

expected := &VerifierValidationConfig{
expected := &WalletPresentationValidationConfig{
VerifierDID: "did:oin:abc",
Metadata: getDefaultMetadata(t, now),
}
Expand Down Expand Up @@ -137,9 +137,9 @@ func TestService_ValidateVerifier(t *testing.T) {
httpClient: http.DefaultClient,
}

err := s.ValidateVerifier(tt.fields.url, tt.args.verifierDID, tt.args.getPresentationCredentials(t, now))
err := s.ValidateWalletPresentation(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)
t.Errorf("ValidateWalletPresentation() error = %v, wantErr %v", err, tt.wantErr)
}

if tt.wantErr {
Expand Down Expand Up @@ -199,21 +199,21 @@ func getDefaultMetadata(t *testing.T, now time.Time) []*CredentialMetadata {
return []*CredentialMetadata{
{
CredentialID: "credentialID1",
Types: []string{
CredentialTypes: []string{
"VerifiableCredential",
},
Issuer: "issuerID1",
Issued: now.Format(time.RFC3339Nano),
Expired: now.Add(time.Hour).Format(time.RFC3339Nano),
IssuerID: "issuerID1",
IssuanceDate: now.Format(time.RFC3339Nano),
ExpirationDate: now.Add(time.Hour).Format(time.RFC3339Nano),
},
{
CredentialID: "credentialID2",
Types: []string{
CredentialTypes: []string{
walletAttestationVCType,
},
Issuer: "issuerID2",
Issued: now.Format(time.RFC3339Nano),
Expired: now.Add(time.Hour).Format(time.RFC3339Nano),
IssuerID: "issuerID2",
IssuanceDate: now.Format(time.RFC3339Nano),
ExpirationDate: now.Add(time.Hour).Format(time.RFC3339Nano),
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (s *Service) RunOIDC4VPFlow(ctx context.Context, authorizationRequest strin

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

if err = trustRegistry.ValidateVerifier(trustRegistryURL, s.vpFlowExecutor.requestObject.ClientID, credentials); err != nil {
if err = trustRegistry.ValidateWalletPresentation(trustRegistryURL, s.vpFlowExecutor.requestObject.ClientID, credentials); err != nil {
return fmt.Errorf("trust registry verifier validation: %w", err)
}
}
Expand Down
14 changes: 12 additions & 2 deletions pkg/service/clientattestation/client_attestation_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ type vcStatusVerifier interface {
ValidateVCStatus(ctx context.Context, vcStatus *verifiable.TypedID, issuer *verifiable.Issuer) error
}

type TrustRegistryPayloadBuilder func(attestationVC *verifiable.Credential, vp *verifiable.Presentation) ([]byte, error)
type TrustRegistryPayloadBuilder func(
clientDID string, attestationVC *verifiable.Credential, vp *verifiable.Presentation) ([]byte, error)

// Config defines configuration for Service.
type Config struct {
Expand Down Expand Up @@ -59,11 +60,20 @@ func NewService(config *Config) *Service {

// ValidateAttestationJWTVP validates attestation VP in jwt_vp format.
//
// Arguments:
//
// jwtVP - presentation contains attestation VC
// policyURL - Trust Registry policy URL
// clientDID - DID identifier.
//
// payloadBuilder - payload builder function.
//
//nolint:revive
func (s *Service) ValidateAttestationJWTVP(
ctx context.Context,
jwtVP string,
policyURL string,
clientDID string,
payloadBuilder TrustRegistryPayloadBuilder,
) error {
vp, err := verifiable.ParsePresentation(
Expand Down Expand Up @@ -116,7 +126,7 @@ func (s *Service) ValidateAttestationJWTVP(
}

var trustRegistryRequestBody []byte
trustRegistryRequestBody, err = payloadBuilder(attestationVC, vp)
trustRegistryRequestBody, err = payloadBuilder(clientDID, attestationVC, vp)
if err != nil {
return fmt.Errorf("payload builder: %w", err)
}
Expand Down
31 changes: 18 additions & 13 deletions pkg/service/clientattestation/client_attestation_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const (
attestationKeyID = "did:example:attestation-service#attestation-key-id"

walletDID = "did:example:wallet"
verifierDID = "did:example:verifier"
walletKeyID = "did:example:wallet#wallet-key-id"
)

Expand All @@ -48,6 +49,7 @@ func TestService_ValidateClientAttestationJWTVP(t *testing.T) {
vcStatusVerifier := NewMockVCStatusVerifier(gomock.NewController(t))

var jwtVP string
var clientDID string
var payloadBuilder clientattestation.TrustRegistryPayloadBuilder

proofCreators, defaultProofChecker := testsupport.NewKMSSignersAndVerifier(t,
Expand Down Expand Up @@ -117,6 +119,8 @@ func TestService_ValidateClientAttestationJWTVP(t *testing.T) {
name: "success OIDC4VP",
url: srv.URL + "/success_oidc4vp",
setup: func() {
clientDID = verifierDID

// create wallet attestation VC with wallet DID as subject and attestation DID as issuer
attestationVC := createAttestationVC(t, attestationProofCreator, walletDID, now, false)

Expand All @@ -133,15 +137,16 @@ func TestService_ValidateClientAttestationJWTVP(t *testing.T) {
payloadBuilder = clientattestation.VerifierInteractionTrustRegistryPayloadBuilder

handler.Add(http.MethodPost, "/success_oidc4vp", func(c echo.Context) error {
var got *clientattestation.VerifierInteractionValidationConfig
var got *clientattestation.VerifierPresentationValidationConfig
assert.NoError(t, c.Bind(&got))

attestationVCUniversalForm, err := attestationVC.ToUniversalForm()
attestationVCJWT, err := attestationVC.ToJWTString()
assert.NoError(t, err)

expected := &clientattestation.VerifierInteractionValidationConfig{
AttestationVC: attestationVCUniversalForm,
Metadata: []*clientattestation.CredentialMetadata{
expected := &clientattestation.VerifierPresentationValidationConfig{
AttestationVC: []string{attestationVCJWT},
VerifierDID: verifierDID,
RequestedVCMetadata: []*clientattestation.CredentialMetadata{
getAttestationVCMetadata(t, now, false),
getRequestedVCMetadata(t, now),
},
Expand Down Expand Up @@ -241,7 +246,7 @@ func TestService_ValidateClientAttestationJWTVP(t *testing.T) {

vcStatusVerifier.EXPECT().ValidateVCStatus(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)

payloadBuilder = func(_ *verifiable.Credential, _ *verifiable.Presentation) ([]byte, error) {
payloadBuilder = func(_ string, _ *verifiable.Credential, _ *verifiable.Presentation) ([]byte, error) {
return nil, errors.New("some error")
}
},
Expand Down Expand Up @@ -310,7 +315,7 @@ func TestService_ValidateClientAttestationJWTVP(t *testing.T) {
ProofChecker: proofChecker,
VCStatusVerifier: vcStatusVerifier,
},
).ValidateAttestationJWTVP(context.Background(), jwtVP, tt.url, payloadBuilder),
).ValidateAttestationJWTVP(context.Background(), jwtVP, tt.url, clientDID, payloadBuilder),
)
})
}
Expand Down Expand Up @@ -440,9 +445,9 @@ func getAttestationVCMetadata(t *testing.T, now time.Time, expired bool) *client
"VerifiableCredential",
"WalletAttestationCredential",
},
Issuer: attestationDID,
Issued: now.Format(time.RFC3339),
Expired: exp,
IssuerID: attestationDID,
Issued: now.Format(time.RFC3339),
Expired: exp,
}
}

Expand All @@ -454,8 +459,8 @@ func getRequestedVCMetadata(t *testing.T, now time.Time) *clientattestation.Cred
Types: []string{
"VerifiableCredential",
},
Issuer: walletDID,
Issued: now.Round(time.Second).Format(time.RFC3339),
Expired: now.Round(time.Second).Add(-time.Hour).Format(time.RFC3339),
IssuerID: walletDID,
Issued: now.Round(time.Second).Format(time.RFC3339),
Expired: now.Round(time.Second).Add(-time.Hour).Format(time.RFC3339),
}
}
26 changes: 18 additions & 8 deletions pkg/service/clientattestation/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,27 @@ type IssuerInteractionValidationConfig struct {
Metadata []*CredentialMetadata `json:"metadata"`
}

type VerifierInteractionValidationConfig struct {
AttestationVC interface{} `json:"attestation_vc"`
Metadata []*CredentialMetadata `json:"metadata"`
// VerifierPresentationValidationConfig represents DTO for verifier presentation policy evaluation.
type VerifierPresentationValidationConfig struct {
// Attestation verifiable credential(s).
AttestationVC []string `json:"attestation_vc"`
// Verifier DID.
VerifierDID string `json:"verifier_did"`
// Credential metadata for Requested VCs only.
RequestedVCMetadata []*CredentialMetadata `json:"credential_metadata"`
}

type CredentialMetadata struct {
CredentialID string `json:"credential_id"`
Types []string `json:"types"`
Issuer string `json:"issuer"`
Issued string `json:"issued"`
Expired string `json:"expired"`
// Credential ID
CredentialID string `json:"credential_id"`
// Credential Types.
Types []string `json:"credential_types"`
// Issuer ID.
IssuerID string `json:"issuer_id"`
// Issuance date/time.
Issued string `json:"issuance_date"`
// Expiration date/time.
Expired string `json:"expiration_date"`
}

type TrustRegistryResponse struct {
Expand Down
18 changes: 11 additions & 7 deletions pkg/service/clientattestation/trustregistry.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const (

// TODO: update payloads
func IssuerInteractionTrustRegistryPayloadBuilder(
_ string,
attestationVC *verifiable.Credential,
presentation *verifiable.Presentation,
) ([]byte, error) {
Expand Down Expand Up @@ -54,25 +55,28 @@ func IssuerInteractionTrustRegistryPayloadBuilder(
return reqPayload, nil
}

// TODO: update payloads
// VerifierInteractionTrustRegistryPayloadBuilder builds Trust Registry payload for Verifier interaction verification.
func VerifierInteractionTrustRegistryPayloadBuilder(
verifierDID string,
attestationVC *verifiable.Credential,
presentation *verifiable.Presentation,
) ([]byte, error) {
credentials := presentation.Credentials()

presentationValidationConfig := &VerifierInteractionValidationConfig{
Metadata: make([]*CredentialMetadata, len(credentials)),
presentationValidationConfig := &VerifierPresentationValidationConfig{
AttestationVC: make([]string, 1),
VerifierDID: verifierDID,
RequestedVCMetadata: make([]*CredentialMetadata, len(credentials)),
}

if uf, err := attestationVC.ToUniversalForm(); err == nil {
presentationValidationConfig.AttestationVC = uf
if attestationVCJWT, err := attestationVC.ToJWTString(); err == nil {
presentationValidationConfig.AttestationVC[0] = attestationVCJWT
}

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

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

reqPayload, err := json.Marshal(presentationValidationConfig)
Expand Down Expand Up @@ -125,7 +129,7 @@ func getCredentialMetadata(content verifiable.CredentialContents) *CredentialMet
return &CredentialMetadata{
CredentialID: content.ID,
Types: content.Types,
Issuer: content.Issuer.ID,
IssuerID: content.Issuer.ID,
Issued: iss,
Expired: exp,
}
Expand Down
Loading

0 comments on commit 95cdf61

Please sign in to comment.