diff --git a/pkg/doc/verifiable/credential_sdjwt.go b/pkg/doc/verifiable/credential_sdjwt.go index 127cefe0f..b7b1fcdb7 100644 --- a/pkg/doc/verifiable/credential_sdjwt.go +++ b/pkg/doc/verifiable/credential_sdjwt.go @@ -15,6 +15,7 @@ import ( "github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/common" "github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/holder" "github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/issuer" + json2 "github.com/hyperledger/aries-framework-go/pkg/doc/util/json" ) type marshalDisclosureOpts struct { @@ -389,6 +390,56 @@ func (vc *Credential) CreateDisplayCredential( // nolint:funlen,gocyclo return newVC, nil } +// CreateDisplayCredentialMap creates, for SD-JWT credentials, a Credential whose selective-disclosure subject fields +// are replaced with the disclosure data. +// +// Options may be provided to filter the disclosures that will be included in the display credential. If a disclosure is +// not included, the associated claim will not be present in the returned credential. +// +// If the calling Credential is not an SD-JWT credential, this method returns the credential itself. +func (vc *Credential) CreateDisplayCredentialMap( // nolint:funlen,gocyclo + opts ...DisplayCredentialOption, +) (map[string]interface{}, error) { + options := &displayCredOpts{} + + for _, opt := range opts { + opt(options) + } + + if options.displayAll && len(options.displayGiven) > 0 { + return nil, fmt.Errorf("incompatible options provided") + } + + if vc.SDJWTHashAlg == "" || vc.JWT == "" { + bytes, err := vc.MarshalJSON() + if err != nil { + return nil, err + } + + return json2.ToMap(bytes) + } + + credClaims, err := unmarshalJWSClaims(vc.JWT, false, nil) + if err != nil { + return nil, fmt.Errorf("unmarshal VC JWT claims: %w", err) + } + + credClaims.refineFromJWTClaims() + + useDisclosures := filterDisclosureList(vc.SDJWTDisclosures, options) + + newVCObj, err := common.GetDisclosedClaims(useDisclosures, credClaims.VC) + if err != nil { + return nil, fmt.Errorf("assembling disclosed claims into vc: %w", err) + } + + if subj, ok := newVCObj["credentialSubject"].(map[string]interface{}); ok { + clearEmpty(subj) + } + + return newVCObj, nil +} + func filterDisclosureList(disclosures []*common.DisclosureClaim, options *displayCredOpts) []*common.DisclosureClaim { if options.displayAll { return disclosures diff --git a/pkg/doc/verifiable/credential_sdjwt_test.go b/pkg/doc/verifiable/credential_sdjwt_test.go index 59fb273b9..4636434c1 100644 --- a/pkg/doc/verifiable/credential_sdjwt_test.go +++ b/pkg/doc/verifiable/credential_sdjwt_test.go @@ -379,6 +379,21 @@ func TestCreateDisplayCredential(t *testing.T) { require.Equal(t, expectedFields, subj[0].CustomFields) }) + t.Run("not a SD-JWT credential map", func(t *testing.T) { + vc2, err := parseTestCredential(t, []byte(jwtTestCredential)) + require.NoError(t, err) + + displayVC, err := vc2.CreateDisplayCredentialMap(DisplayAllDisclosures()) + require.NoError(t, err) + require.NotEmpty(t, displayVC) + }) + + t.Run("display all claims map", func(t *testing.T) { + displayVC, err := vc.CreateDisplayCredentialMap(DisplayAllDisclosures()) + require.NoError(t, err) + require.NotEmpty(t, displayVC) + }) + t.Run("display no claims", func(t *testing.T) { displayVC, err := vc.CreateDisplayCredential() require.NoError(t, err)