diff --git a/pkg/doc/cm/common_test.go b/pkg/doc/cm/common_test.go new file mode 100644 index 000000000..937792e51 --- /dev/null +++ b/pkg/doc/cm/common_test.go @@ -0,0 +1,41 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package cm_test + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" + "github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil" +) + +const errorMessageTestNameFormat = "Test name: %s" + +// Marshals the presentation and then unmarshals it again so that the type of the custom fields matches the type of +// the expected presentation - this allows us to use reflect.DeepEqual to compare them. +func marshalThenUnmarshalAgain(t *testing.T, presentation *verifiable.Presentation, + testName string) *verifiable.Presentation { + presentationBytes, err := json.Marshal(presentation) + require.NoError(t, err, errorMessageTestNameFormat, testName) + + return makePresentationFromBytes(t, presentationBytes, testName) +} + +func makePresentationFromBytes(t *testing.T, presentationBytes []byte, testName string) *verifiable.Presentation { + loader, err := ldtestutil.DocumentLoader() + require.NoError(t, err) + + presentation, err := verifiable.ParsePresentation(presentationBytes, + verifiable.WithPresDisabledProofCheck(), + verifiable.WithPresJSONLDDocumentLoader(loader)) + require.NoError(t, err, errorMessageTestNameFormat, testName) + + return presentation +} diff --git a/pkg/doc/cm/credentialapplication.go b/pkg/doc/cm/credentialapplication.go index 6447d0b6b..46ddbba1e 100644 --- a/pkg/doc/cm/credentialapplication.go +++ b/pkg/doc/cm/credentialapplication.go @@ -11,14 +11,27 @@ import ( "errors" "fmt" + "github.com/google/uuid" + "github.com/hyperledger/aries-framework-go/pkg/doc/presexch" + "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" +) + +const ( + credentialApplicationPresentationContext = "https://identity.foundation/credential-manifest/application/v1" + credentialApplicationPresentationType = "CredentialApplication" ) // CredentialApplication represents a credential_application object as defined in // https://identity.foundation/credential-manifest/#credential-application. -// When used in an envelope like a Verifiable Presentation (under a field named "credential_application", that -// envelope then becomes a Credential Application. -// This object may also have a sibling presentation_submission object within the envelope. +// Note that the term "Credential Application" is overloaded in the spec - a "Credential Application" may be referring +// to one of two different, but related, concepts. A "Credential Application" can be the object defined below, which is +// intended to be embedded in an envelope like a Verifiable Presentation. Additionally, when that envelope contains +// the object defined below under a field named "credential_application", then that envelope itself can be called +// a "Credential Application". The larger "envelope version" of a Credential Application may also have a sibling +// presentation_submission object within the envelope, as demonstrated by the PresentCredentialApplication method. +// See https://github.com/decentralized-identity/credential-manifest/issues/73 for more information about this name +// overloading. type CredentialApplication struct { ID string `json:"id,omitempty"` // The value of this property MUST be the ID of a valid Credential Manifest. @@ -158,6 +171,141 @@ func (ca *CredentialApplication) ensureFormatIsSubsetOfCredManifestFormat(credMa return nil } +// presentCredentialApplicationOpts holds options for the PresentCredentialApplication method. +type presentCredentialApplicationOpts struct { + existingPresentation verifiable.Presentation + existingPresentationSet bool +} + +// PresentCredentialApplicationOpt is an option for the PresentCredentialApplication method. +type PresentCredentialApplicationOpt func(opts *presentCredentialApplicationOpts) + +// WithExistingPresentationForPresentCredentialApplication is an option for the PresentCredentialApplication method +// that allows Credential Application data to be added to an existing Presentation +// (turning it into a Credential Application in the process). The existing Presentation should not already have +// Credential Application data. +func WithExistingPresentationForPresentCredentialApplication( + presentation *verifiable.Presentation) PresentCredentialApplicationOpt { + return func(opts *presentCredentialApplicationOpts) { + opts.existingPresentation = *presentation + opts.existingPresentationSet = true + } +} + +// PresentCredentialApplication creates a minimal Presentation (without proofs) with Credential Application data based +// on credentialManifest. The WithExistingPresentationForPresentCredentialFulfillment can be used to add the Credential +// Application data to an existing Presentation object instead. If the +// "https://identity.foundation/presentation-exchange/submission/v1" context is found, it will be replaced with +// the "https://identity.foundation/credential-manifest/application/v1" context. Note that any existing proofs are +// not updated. Note also the following assumptions/limitations of this method: +// 1. The format of all claims in the Presentation Submission are assumed to be ldp_vp and will be set as such. +// 2. The format for the Credential Application object will be set to match the format from the Credential Manifest +// exactly. If a caller wants to use a smaller subset of the Credential Manifest's format, then they will have to +// set it manually. +// 2. The location of the Verifiable Credentials is assumed to be an array at the root under a field called +// "verifiableCredential". +// 3. The Verifiable Credentials in the presentation is assumed to be in the same order as the Output Descriptors in +// the Credential Manifest. +func PresentCredentialApplication(credentialManifest *CredentialManifest, + opts ...PresentCredentialApplicationOpt) (*verifiable.Presentation, error) { + if credentialManifest == nil { + return nil, errors.New("credential manifest argument cannot be nil") + } + + appliedOptions := getPresentCredentialApplicationOpts(opts) + + var presentation verifiable.Presentation + + if appliedOptions.existingPresentationSet { + presentation = appliedOptions.existingPresentation + } else { + newPresentation, err := verifiable.NewPresentation() + if err != nil { + return nil, err + } + + presentation = *newPresentation + } + + setCredentialApplicationContext(&presentation) + + presentation.Type = append(presentation.Type, credentialApplicationPresentationType) + + setCustomFields(&presentation, credentialManifest) + + return &presentation, nil +} + +func getPresentCredentialApplicationOpts(opts []PresentCredentialApplicationOpt) *presentCredentialApplicationOpts { + processedOptions := &presentCredentialApplicationOpts{} + + for _, opt := range opts { + if opt != nil { + opt(processedOptions) + } + } + + return processedOptions +} + +func setCredentialApplicationContext(presentation *verifiable.Presentation) { + var newContextSet bool + + for i := range presentation.Context { + if presentation.Context[i] == presexch.PresentationSubmissionJSONLDContextIRI { + presentation.Context[i] = credentialApplicationPresentationContext + newContextSet = true + + break + } + } + + if !newContextSet { + presentation.Context = append(presentation.Context, credentialApplicationPresentationContext) + } +} + +func setCustomFields(presentation *verifiable.Presentation, credentialManifest *CredentialManifest) { + application := CredentialApplication{ + ID: uuid.New().String(), + ManifestID: credentialManifest.ID, + Format: credentialManifest.Format, + } + + if presentation.CustomFields == nil { + presentation.CustomFields = make(map[string]interface{}) + } + + presentation.CustomFields["credential_application"] = application + + if credentialManifest.PresentationDefinition != nil { + submission := makePresentationSubmission(credentialManifest.PresentationDefinition) + + presentation.CustomFields["presentation_submission"] = submission + } +} + +func makePresentationSubmission(presentationDef *presexch.PresentationDefinition) presexch.PresentationSubmission { + descriptorMap := make([]*presexch.InputDescriptorMapping, + len(presentationDef.InputDescriptors)) + + for i := range presentationDef.InputDescriptors { + descriptorMap[i] = &presexch.InputDescriptorMapping{ + ID: presentationDef.InputDescriptors[i].ID, + Format: "ldp_vp", + Path: fmt.Sprintf("$.verifiableCredential[%d]", i), + } + } + + submission := presexch.PresentationSubmission{ + ID: uuid.New().String(), + DefinitionID: presentationDef.ID, + DescriptorMap: descriptorMap, + } + + return submission +} + func ensureCredAppJWTAlgsAreSubsetOfCredManiJWTAlgs(algType string, credAppJWTType, credManifestJWTType *presexch.JwtType) error { if credAppJWTType != nil { //nolint:nestif // hard to resolve without creating a worse issue diff --git a/pkg/doc/cm/credentialapplication_test.go b/pkg/doc/cm/credentialapplication_test.go index 5c17c200a..6d8b79ddc 100644 --- a/pkg/doc/cm/credentialapplication_test.go +++ b/pkg/doc/cm/credentialapplication_test.go @@ -9,35 +9,71 @@ package cm_test import ( _ "embed" "encoding/json" + "reflect" "testing" "github.com/stretchr/testify/require" "github.com/hyperledger/aries-framework-go/pkg/doc/cm" + "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" ) const unknownFormatName = "SomeUnknownFormat" -//go:embed testdata/credential_application_university_degree.json -var validCredentialApplication []byte //nolint:gochecknoglobals +// Note that the term "Credential Application" can refer to two different, but related, concepts. See the +// documentation above the cm.CredentialApplication type definition for more information. + +// Sample Credential Applications for a university degree. +// Here, "Credential Application" refers to the "credential_application" object that gets embedded within a larger +// envelope. +var ( + //go:embed testdata/credential_application_university_degree.json + credentialApplicationUniversityDegree []byte //nolint:gochecknoglobals + //go:embed testdata/credential_application_university_degree_with_format.json + credentialApplicationUniversityDegreeWithFormat []byte //nolint:gochecknoglobals +) + +// Sample "minimal" Verifiable Presentations. These are VPs that were created by a call to verifiable.NewPresentation() +// with no arguments/options, which is how the cm.PresentCredentialApplication method generates a VP if the +// WithExistingPresentationForPresentCredentialApplication option is not used. Additional data +// (like Credential Application and Credential Submission) was then added to it. +var ( + //go:embed testdata/VP_minimal_with_credential_application.json + vpMinimalWithCredentialApplication []byte //nolint:gochecknoglobals + //go:embed testdata/VP_minimal_with_credential_application_and_presentation_submission.json + vpMinimalWithCredentialApplicationAndPresentationSubmission []byte //nolint:gochecknoglobals + //go:embed testdata/VP_minimal_with_credential_application_and_presentation_submission_and_format.json + vpMinimalWithCredentialApplicationAndPresentationSubmissionAndFormat []byte //nolint:gochecknoglobals +) -//go:embed testdata/credential_application_university_degree_with_format.json -var validCredentialApplicationWithFormat []byte //nolint:gochecknoglobals +// Sample Verifiable Presentations that contain a PR card VC. +var ( + //go:embed testdata/VP_with_PR_Card_VC.json + vpWithPRCardVC []byte //nolint:gochecknoglobals + //go:embed testdata/VP_with_PR_card_VC_and_credential_application.json + vpWithPRCardVCAndCredentialApplication []byte //nolint:gochecknoglobals + //go:embed testdata/VP_with_PR_card_VC_and_credential_application_and_presentation_submission.json + vpWithPRCardVCAndCredentialApplicationAndPresentationSubmission []byte //nolint:gochecknoglobals + //go:embed testdata/VP_with_PR_card_VC_and_credential_application_and_presentation_submission_and_format.json + vpWithPRCardVCAndCredentialApplicationAndPresentationSubmissionAndFormat []byte //nolint:gochecknoglobals + //go:embed testdata/VP_with_PR_Card_VC_using_presentation_exchange_context.json + vpWithPRCardVCUsingPresentationExchangeContext []byte //nolint:gochecknoglobals +) func TestUnmarshalAndValidateAgainstCredentialManifest(t *testing.T) { t.Run("Success", func(t *testing.T) { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialApplication, err := cm.UnmarshalAndValidateAgainstCredentialManifest( - validCredentialApplication, &credentialManifest) + credentialApplicationUniversityDegree, &credentialManifest) require.NoError(t, err) require.Equal(t, "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", credentialApplication.ID) }) t.Run("Failure during validation", func(t *testing.T) { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) _, err := cm.UnmarshalAndValidateAgainstCredentialManifest( - validCredentialApplication, &credentialManifest) + credentialApplicationUniversityDegree, &credentialManifest) require.EqualError(t, err, "invalid format for the given Credential Manifest: the Credential "+ "Manifest specifies a format but the Credential Application does not") }) @@ -45,7 +81,7 @@ func TestUnmarshalAndValidateAgainstCredentialManifest(t *testing.T) { func TestCredentialApplication_Unmarshal(t *testing.T) { t.Run("Success", func(t *testing.T) { - makeCredentialApplicationFromBytes(t, validCredentialApplication) + makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegree) }) t.Run("Missing ID", func(t *testing.T) { credentialApplicationBytes := makeCredentialApplicationWithMissingID(t) @@ -68,9 +104,9 @@ func TestCredentialApplication_Unmarshal(t *testing.T) { func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { t.Run("Credential Manifest has no format and no presentation definition", func(t *testing.T) { t.Run("Credential Application has no format and no presentation definition", func(t *testing.T) { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplication) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegree) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.NoError(t, err) @@ -78,9 +114,9 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { }) t.Run("Credential Manifest has a format", func(t *testing.T) { t.Run("Credential Application has no format", func(t *testing.T) { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplication) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegree) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.EqualError(t, err, "invalid format for the given Credential Manifest: the Credential "+ @@ -89,7 +125,7 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { t.Run("Credential App requests a JWT format not allowed by the Credential Manifest", func(t *testing.T) { credentialApplication := makeCredentialApplicationWithUnknownJWTAlg(t) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.EqualError(t, err, "invalid format for the given Credential Manifest: invalid format "+ @@ -100,7 +136,7 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { t.Run("Cred App requests a JWT VC format not allowed by the Cred Manifest", func(t *testing.T) { credentialApplication := makeCredentialApplicationWithUnknownJWTVCAlg(t) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.EqualError(t, err, "invalid format for the given Credential Manifest: invalid format "+ @@ -111,7 +147,7 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { t.Run("Cred App requests a JWT VP format not allowed by the Cred Manifest", func(t *testing.T) { credentialApplication := makeCredentialApplicationWithUnknownJWTVPAlg(t) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.EqualError(t, err, "invalid format for the given Credential Manifest: invalid format "+ @@ -122,7 +158,7 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { t.Run("Cred App requests an LDP proof type not allowed by the Cred Manifest", func(t *testing.T) { credentialApplication := makeCredentialApplicationWithUnknownLDPProofType(t) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.EqualError(t, err, "invalid format for the given Credential Manifest: invalid format "+ @@ -133,7 +169,7 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { t.Run("Cred App requests an LDP VC proof type not allowed by the Cred Manifest", func(t *testing.T) { credentialApplication := makeCredentialApplicationWithUnknownLDPVCProofType(t) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.EqualError(t, err, "invalid format for the given Credential Manifest: invalid format "+ @@ -145,7 +181,7 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { t.Run("Cred App requests an LDP VC proof type not allowed by the Cred Manifest", func(t *testing.T) { credentialApplication := makeCredentialApplicationWithUnknownLDPVPProofType(t) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.EqualError(t, err, "invalid format for the given Credential Manifest: invalid format "+ @@ -154,7 +190,7 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { "[Ed25519Signature2018]") }) t.Run("Cred App requests JWT formats but the Cred Manifest's JWT format is nil", func(t *testing.T) { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) credentialManifest := createCredentialManifestWithNilJWTFormat(t) @@ -164,7 +200,7 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { "One or more of these are not in the Credential Manifest's supported JWT algorithms: []") }) t.Run("Cred App requests JWT formats but the Cred Manifest's LDP format is nil", func(t *testing.T) { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) credentialManifest := createCredentialManifestWithNilLDPFormat(t) @@ -174,9 +210,9 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { "or more of these are not in the Credential Manifest's supported LDP proof types: []") }) t.Run("Credential Application has a valid format", func(t *testing.T) { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.NoError(t, err) @@ -185,7 +221,7 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { t.Run("Credential App's manifest ID does not match the given Credential Manifest", func(t *testing.T) { credentialApplication := makeCredentialApplicationWithUnknownManifestID(t) - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) err := credentialApplication.ValidateAgainstCredentialManifest(&credentialManifest) require.EqualError(t, err, "the Manifest ID of the Credential Application (SomeUnknownManifestID) "+ @@ -193,6 +229,115 @@ func TestCredentialApplication_ValidateAgainstCredentialManifest(t *testing.T) { }) } +func TestPresentCredentialApplication(t *testing.T) { + t.Run("Successes", func(t *testing.T) { + testTable := map[string]struct { + existingPresentation []byte + credentialManifest []byte + expectedPresentation []byte + }{ + "Without existing presentation, Credential Manifest has no Presentation Definition and no format": { + existingPresentation: nil, + credentialManifest: credentialManifestDriversLicense, + expectedPresentation: vpMinimalWithCredentialApplication, + }, + "With existing presentation, Credential Manifest has no Presentation Definition and no format": { + existingPresentation: vpWithPRCardVC, + credentialManifest: credentialManifestDriversLicense, + expectedPresentation: vpWithPRCardVCAndCredentialApplication, + }, + "Without existing presentation, Credential Manifest has a Presentation Definition but no format": { + existingPresentation: nil, + credentialManifest: credentialManifestDriversLicenseWithPresentationDefinition, + expectedPresentation: vpMinimalWithCredentialApplicationAndPresentationSubmission, + }, + "With existing presentation, Credential Manifest has a Presentation Definition but no format": { + existingPresentation: vpWithPRCardVC, + credentialManifest: credentialManifestDriversLicenseWithPresentationDefinition, + expectedPresentation: vpWithPRCardVCAndCredentialApplicationAndPresentationSubmission, + }, + "Without existing presentation, Credential Manifest has a Presentation Definition and format": { + existingPresentation: nil, + credentialManifest: credentialManifestDriversLicenseWithPresentationDefinitionAndFormat, + expectedPresentation: vpMinimalWithCredentialApplicationAndPresentationSubmissionAndFormat, + }, + "With existing presentation, Credential Manifest has a Presentation Definition and format": { + existingPresentation: vpWithPRCardVC, + credentialManifest: credentialManifestDriversLicenseWithPresentationDefinitionAndFormat, + expectedPresentation: vpWithPRCardVCAndCredentialApplicationAndPresentationSubmissionAndFormat, + }, + "With existing presentation that uses Presentation Exchange context, " + + "Credential Manifest has no Presentation Definition and no format": { + existingPresentation: vpWithPRCardVCUsingPresentationExchangeContext, + credentialManifest: credentialManifestDriversLicense, + expectedPresentation: vpWithPRCardVCAndCredentialApplication, + }, + } + + for testName, testData := range testTable { + credentialManifest := makeCredentialManifestFromBytes(t, testData.credentialManifest) + + var option cm.PresentCredentialApplicationOpt + + if testData.existingPresentation != nil { + existingPresentation := makePresentationFromBytes(t, testData.existingPresentation, testName) + + option = cm.WithExistingPresentationForPresentCredentialApplication(existingPresentation) + } + + presentation, err := cm.PresentCredentialApplication(&credentialManifest, option) + require.NoError(t, err, errorMessageTestNameFormat, testName) + require.NotNil(t, presentation, errorMessageTestNameFormat, testName) + + reunmarshalledPresentation := marshalThenUnmarshalAgain(t, presentation, testName) + + expectedPresentation := makePresentationFromBytes(t, testData.expectedPresentation, testName) + + makeCredentialApplicationIDsTheSame(t, reunmarshalledPresentation, expectedPresentation, testName) + makePresentationSubmissionIDsTheSame(reunmarshalledPresentation, expectedPresentation) + + require.True(t, reflect.DeepEqual(reunmarshalledPresentation, expectedPresentation), errorMessageTestNameFormat+ + "the presentation with a Credential Fulfillment added to it differs from what was expected", testName) + } + }) + t.Run("Nil Credential Manifest argument", func(t *testing.T) { + presentation, err := cm.PresentCredentialApplication(nil) + require.EqualError(t, err, "credential manifest argument cannot be nil") + require.Nil(t, presentation) + }) +} + +func makeCredentialApplicationIDsTheSame(t *testing.T, presentation1, + presentation2 *verifiable.Presentation, testName string) { + credentialApplicationFromPresentation1, ok := + presentation1.CustomFields["credential_application"].(map[string]interface{}) + require.True(t, ok, errorMessageTestNameFormat, testName) + + credentialApplicationFromPresentation2, ok := + presentation2.CustomFields["credential_application"].(map[string]interface{}) + require.True(t, ok, errorMessageTestNameFormat, testName) + + credentialApplicationFromPresentation2["id"] = credentialApplicationFromPresentation1["id"] +} + +// If either presentation is missing a presentation_submission field, then this function returns without +// changing anything. +func makePresentationSubmissionIDsTheSame(presentation1, presentation2 *verifiable.Presentation) { + credentialSubmissionFromPresentation1, ok := + presentation1.CustomFields["presentation_submission"].(map[string]interface{}) + if !ok { + return + } + + credentialSubmissionFromPresentation2, ok := + presentation2.CustomFields["presentation_submission"].(map[string]interface{}) + if !ok { + return + } + + credentialSubmissionFromPresentation2["id"] = credentialSubmissionFromPresentation1["id"] +} + func makeCredentialApplicationFromBytes(t *testing.T, credentialApplicationBytes []byte) cm.CredentialApplication { var credentialApplication cm.CredentialApplication @@ -204,7 +349,7 @@ func makeCredentialApplicationFromBytes(t *testing.T, } func makeCredentialApplicationWithMissingID(t *testing.T) []byte { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplication) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegree) credentialApplication.ID = "" @@ -215,7 +360,7 @@ func makeCredentialApplicationWithMissingID(t *testing.T) []byte { } func makeCredentialApplicationWithMissingManifestID(t *testing.T) []byte { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplication) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegree) credentialApplication.ManifestID = "" @@ -226,7 +371,7 @@ func makeCredentialApplicationWithMissingManifestID(t *testing.T) []byte { } func makeCredentialApplicationWithUnknownJWTAlg(t *testing.T) cm.CredentialApplication { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) credentialApplication.Format.Jwt.Alg[0] = unknownFormatName @@ -234,7 +379,7 @@ func makeCredentialApplicationWithUnknownJWTAlg(t *testing.T) cm.CredentialAppli } func makeCredentialApplicationWithUnknownJWTVCAlg(t *testing.T) cm.CredentialApplication { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) credentialApplication.Format.JwtVC.Alg[0] = unknownFormatName @@ -242,7 +387,7 @@ func makeCredentialApplicationWithUnknownJWTVCAlg(t *testing.T) cm.CredentialApp } func makeCredentialApplicationWithUnknownJWTVPAlg(t *testing.T) cm.CredentialApplication { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) credentialApplication.Format.JwtVP.Alg[0] = unknownFormatName @@ -250,7 +395,7 @@ func makeCredentialApplicationWithUnknownJWTVPAlg(t *testing.T) cm.CredentialApp } func makeCredentialApplicationWithUnknownLDPProofType(t *testing.T) cm.CredentialApplication { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) credentialApplication.Format.Ldp.ProofType[0] = unknownFormatName @@ -258,7 +403,7 @@ func makeCredentialApplicationWithUnknownLDPProofType(t *testing.T) cm.Credentia } func makeCredentialApplicationWithUnknownLDPVCProofType(t *testing.T) cm.CredentialApplication { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) credentialApplication.Format.LdpVC.ProofType[0] = unknownFormatName @@ -266,7 +411,7 @@ func makeCredentialApplicationWithUnknownLDPVCProofType(t *testing.T) cm.Credent } func makeCredentialApplicationWithUnknownLDPVPProofType(t *testing.T) cm.CredentialApplication { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) credentialApplication.Format.LdpVP.ProofType[0] = unknownFormatName @@ -274,7 +419,7 @@ func makeCredentialApplicationWithUnknownLDPVPProofType(t *testing.T) cm.Credent } func makeCredentialApplicationWithUnknownManifestID(t *testing.T) cm.CredentialApplication { - credentialApplication := makeCredentialApplicationFromBytes(t, validCredentialApplicationWithFormat) + credentialApplication := makeCredentialApplicationFromBytes(t, credentialApplicationUniversityDegreeWithFormat) credentialApplication.ManifestID = "SomeUnknownManifestID" diff --git a/pkg/doc/cm/credentialfulfillment.go b/pkg/doc/cm/credentialfulfillment.go index 69442eea7..49768eab1 100644 --- a/pkg/doc/cm/credentialfulfillment.go +++ b/pkg/doc/cm/credentialfulfillment.go @@ -113,10 +113,11 @@ type presentCredentialFulfillmentOpts struct { // PresentCredentialFulfillmentOpt is an option for the PresentCredentialFulfillment method. type PresentCredentialFulfillmentOpt func(opts *presentCredentialFulfillmentOpts) -// WithExistingPresentation is an option for the PresentCredentialFulfillment method that allows Credential Fulfillment -// data to be added to an existing Presentation (turning it into a Credential Fulfillment in the process). -// The existing Presentation should not already have Credential Fulfillment data. -func WithExistingPresentation(presentation *verifiable.Presentation) PresentCredentialFulfillmentOpt { +// WithExistingPresentationForPresentCredentialFulfillment is an option for the PresentCredentialFulfillment method +// that allows Credential Fulfillment data to be added to an existing Presentation (turning it into a Credential +// Fulfillment in the process). The existing Presentation should not already have Credential Fulfillment data. +func WithExistingPresentationForPresentCredentialFulfillment( + presentation *verifiable.Presentation) PresentCredentialFulfillmentOpt { return func(opts *presentCredentialFulfillmentOpts) { opts.existingPresentation = *presentation opts.existingPresentationSet = true @@ -124,9 +125,9 @@ func WithExistingPresentation(presentation *verifiable.Presentation) PresentCred } // PresentCredentialFulfillment creates a basic Presentation (without proofs) with Credential Fulfillment data based -// on credentialManifest. The WithExistingPresentation can be used to add the Credential Fulfillment data to an existing -// Presentation object instead. Note that any existing proofs are not updated. -// Note the following assumptions/limitations of this method: +// on credentialManifest. The WithExistingPresentationForPresentCredentialFulfillment can be used to add the Credential +// Fulfillment data to an existing Presentation object instead. Note that any existing proofs are not updated. +// Note also the following assumptions/limitations of this method: // 1. The format of all credentials is assumed to be ldp_vc. // 2. The location of the Verifiable Credentials is assumed to be an array at the root under a field called // "verifiableCredential". @@ -138,7 +139,7 @@ func PresentCredentialFulfillment(credentialManifest *CredentialManifest, return nil, errors.New("credential manifest argument cannot be nil") } - appliedOptions := getOpts(opts) + appliedOptions := getPresentCredentialFulfillmentOpts(opts) var presentation verifiable.Presentation @@ -180,7 +181,7 @@ func PresentCredentialFulfillment(credentialManifest *CredentialManifest, return &presentation, nil } -func getOpts(opts []PresentCredentialFulfillmentOpt) *presentCredentialFulfillmentOpts { +func getPresentCredentialFulfillmentOpts(opts []PresentCredentialFulfillmentOpt) *presentCredentialFulfillmentOpts { processedOptions := &presentCredentialFulfillmentOpts{} for _, opt := range opts { diff --git a/pkg/doc/cm/credentialfulfillment_test.go b/pkg/doc/cm/credentialfulfillment_test.go index b7ebffd82..00181870d 100644 --- a/pkg/doc/cm/credentialfulfillment_test.go +++ b/pkg/doc/cm/credentialfulfillment_test.go @@ -16,9 +16,7 @@ import ( "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/issuecredential" "github.com/hyperledger/aries-framework-go/pkg/doc/cm" - "github.com/hyperledger/aries-framework-go/pkg/doc/ld" "github.com/hyperledger/aries-framework-go/pkg/doc/verifiable" - "github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil" ) var ( @@ -26,12 +24,15 @@ var ( validCredentialFulfillment []byte //nolint:gochecknoglobals //go:embed testdata/issue_credential_message_university_degree.json validIssueCredentialMessage []byte //nolint:gochecknoglobals - //go:embed testdata/verifiable_presentation_drivers_license_without_credential_fulfillment.json - verifiablePresentationWithoutCredentialFulfillment []byte //nolint:gochecknoglobals - //go:embed testdata/verifiable_presentation_drivers_license_with_credential_fulfillment.json - verifiablePresentationWithCredentialFulfillment []byte //nolint:gochecknoglobals - //go:embed testdata/verifiable_presentation_basic_with_credential_fulfillment.json - verifiablePresentationBasicWithCredentialFulfillment []byte //nolint:gochecknoglobals + //go:embed testdata/VP_with_drivers_license_VC.json + vpWithDriversLicenseVC []byte //nolint:gochecknoglobals + //go:embed testdata/VP_with_drivers_license_VC_and_credential_fulfillment.json + vpWithDriversLicenseVCAndCredentialFulfillment []byte //nolint:gochecknoglobals + // The "minimal" VP below is ones that was created by a call to verifiable.NewPresentation() with no + // arguments/options, which is how the cm.PresentCredentialFulfillment method generates a VP if the + // WithExistingPresentationForPresentCredentialFulfillment option is not used. + //go:embed testdata/VP_minimal_with_credential_fulfillment.json + vpMinimalWithCredentialFulfillment []byte //nolint:gochecknoglobals ) func TestCredentialFulfillment_Unmarshal(t *testing.T) { @@ -103,70 +104,55 @@ func TestCredentialFulfillment_ResolveDescriptorMap(t *testing.T) { }) } -func TestAddCredentialFulfillmentToPresentation(t *testing.T) { - t.Run("Without using WithExistingPresentation option", func(t *testing.T) { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestDriversLicense) +func TestPresentCredentialFulfillment(t *testing.T) { + t.Run("Without using WithExistingPresentationForPresentCredentialFulfillment option", func(t *testing.T) { + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestDriversLicenseWithPresentationDefinition) presentation, err := cm.PresentCredentialFulfillment(&credentialManifest) require.NoError(t, err) require.NotNil(t, presentation) - loader, err := ldtestutil.DocumentLoader() - require.NoError(t, err) - - expectedPresentation, err := verifiable.ParsePresentation(verifiablePresentationBasicWithCredentialFulfillment, - verifiable.WithPresDisabledProofCheck(), - verifiable.WithPresJSONLDDocumentLoader(loader)) - require.NoError(t, err) + expectedPresentation := makePresentationFromBytes(t, vpMinimalWithCredentialFulfillment, + "Present Credential Fulfillment without existing Presentation") - reunmarshalledPresentation := marshalThenUnmarshalAgain(t, presentation, loader) + reunmarshalledPresentation := marshalThenUnmarshalAgain(t, presentation, + "Present Credential Fulfillment without existing presentation") makeCredentialFulfillmentIDsTheSame(t, reunmarshalledPresentation, expectedPresentation) require.True(t, reflect.DeepEqual(reunmarshalledPresentation, expectedPresentation), "the presentation with a Credential Fulfillment added to it differs from what was expected") }) - t.Run("Using WithExistingPresentation option", func(t *testing.T) { + t.Run("Using WithExistingPresentationForPresentCredentialFulfillment option", func(t *testing.T) { t.Run("CustomFields is not nil", func(t *testing.T) { - loader, err := ldtestutil.DocumentLoader() - require.NoError(t, err) + testName := "Present Credential Fulfillment with existing presentation, CustomFields is not nil" - presentation, err := verifiable.ParsePresentation(verifiablePresentationWithoutCredentialFulfillment, - verifiable.WithPresDisabledProofCheck(), - verifiable.WithPresJSONLDDocumentLoader(loader)) - require.NoError(t, err) + presentation := makePresentationFromBytes(t, vpWithDriversLicenseVC, testName) - doPresentCredentialFulfillmentTestWithExistingPresentation(t, presentation, loader) + doPresentCredentialFulfillmentTestWithExistingPresentation(t, presentation, testName) }) t.Run("CustomFields is nil", func(t *testing.T) { - loader, err := ldtestutil.DocumentLoader() - require.NoError(t, err) + testName := "Present Credential Fulfillment with existing presentation, CustomFields is nil" - presentation, err := verifiable.ParsePresentation(verifiablePresentationWithoutCredentialFulfillment, - verifiable.WithPresDisabledProofCheck(), - verifiable.WithPresJSONLDDocumentLoader(loader)) - require.NoError(t, err) + presentation := makePresentationFromBytes(t, vpWithDriversLicenseVC, testName) presentation.CustomFields = nil - doPresentCredentialFulfillmentTestWithExistingPresentation(t, presentation, loader) + doPresentCredentialFulfillmentTestWithExistingPresentation(t, presentation, testName) }) }) } func doPresentCredentialFulfillmentTestWithExistingPresentation(t *testing.T, - presentationToAddCredentialFulfillmentTo *verifiable.Presentation, loader *ld.DocumentLoader) { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestDriversLicense) + presentationToAddCredentialFulfillmentTo *verifiable.Presentation, testName string) { + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestDriversLicenseWithPresentationDefinition) presentationWithAddedCredentialFulfillment, err := cm.PresentCredentialFulfillment(&credentialManifest, - cm.WithExistingPresentation(presentationToAddCredentialFulfillmentTo)) + cm.WithExistingPresentationForPresentCredentialFulfillment(presentationToAddCredentialFulfillmentTo)) require.NoError(t, err) - expectedPresentation, err := verifiable.ParsePresentation(verifiablePresentationWithCredentialFulfillment, - verifiable.WithPresDisabledProofCheck(), - verifiable.WithPresJSONLDDocumentLoader(loader)) - require.NoError(t, err) + expectedPresentation := makePresentationFromBytes(t, vpWithDriversLicenseVCAndCredentialFulfillment, testName) - reunmarshalledPresentation := marshalThenUnmarshalAgain(t, presentationWithAddedCredentialFulfillment, loader) + reunmarshalledPresentation := marshalThenUnmarshalAgain(t, presentationWithAddedCredentialFulfillment, testName) makeCredentialFulfillmentIDsTheSame(t, reunmarshalledPresentation, expectedPresentation) @@ -176,32 +162,17 @@ func doPresentCredentialFulfillmentTestWithExistingPresentation(t *testing.T, // The credential Fulfillment ID is randomly generated in the PresentCredentialFulfillment method, so this method // is useful for allowing two presentations created by that method to be compared using reflect.DeepEqual. -func makeCredentialFulfillmentIDsTheSame(t *testing.T, reunmarshalledPresentation, - expectedPresentation *verifiable.Presentation) { - credentialFulfillmentFromPresentation, ok := - reunmarshalledPresentation.CustomFields["credential_fulfillment"].(map[string]interface{}) +func makeCredentialFulfillmentIDsTheSame(t *testing.T, presentation1, + presentation2 *verifiable.Presentation) { + credentialFulfillmentFromPresentation1, ok := + presentation1.CustomFields["credential_fulfillment"].(map[string]interface{}) require.True(t, ok) - credentialFulfillmentFromExpectedPresentation, ok := - expectedPresentation.CustomFields["credential_fulfillment"].(map[string]interface{}) + credentialFulfillmentFromPresentation2, ok := + presentation2.CustomFields["credential_fulfillment"].(map[string]interface{}) require.True(t, ok) - credentialFulfillmentFromExpectedPresentation["id"] = credentialFulfillmentFromPresentation["id"] -} - -// Marshals the presentation and then unmarshals it again so that the type of the custom fields matches the type of -// the expected presentation - this allows us to use reflect.DeepEqual to compare them. -func marshalThenUnmarshalAgain(t *testing.T, presentation *verifiable.Presentation, - loader *ld.DocumentLoader) *verifiable.Presentation { - presentationBytes, err := json.Marshal(presentation) - require.NoError(t, err) - - reunmarshalledPresentation, err := verifiable.ParsePresentation(presentationBytes, - verifiable.WithPresDisabledProofCheck(), - verifiable.WithPresJSONLDDocumentLoader(loader)) - require.NoError(t, err) - - return reunmarshalledPresentation + credentialFulfillmentFromPresentation2["id"] = credentialFulfillmentFromPresentation1["id"] } func makeValidCredentialFulfillment(t *testing.T) cm.CredentialFulfillment { diff --git a/pkg/doc/cm/credentialmanifest_test.go b/pkg/doc/cm/credentialmanifest_test.go index f696d0bef..73431cb08 100644 --- a/pkg/doc/cm/credentialmanifest_test.go +++ b/pkg/doc/cm/credentialmanifest_test.go @@ -19,15 +19,28 @@ import ( "github.com/hyperledger/aries-framework-go/pkg/internal/ldtestutil" ) +// Sample Credential Manifests for a university degree. var ( //go:embed testdata/credential_manifest_university_degree.json - validCredentialManifestBachelorsDegree []byte //nolint:gochecknoglobals - //go:embed testdata/credential_manifest_drivers_license.json - validCredentialManifestDriversLicense []byte //nolint:gochecknoglobals + credentialManifestUniversityDegree []byte //nolint:gochecknoglobals //go:embed testdata/credential_manifest_university_degree_with_format.json - validCredentialManifestWithFormat []byte //nolint:gochecknoglobals + credentialManifestUniversityDegreeWithFormat []byte //nolint:gochecknoglobals //go:embed testdata/credential_manifest_university_degree_with_presentation_definition.json - validCredentialManifestWithPresentationSubmission []byte //nolint:gochecknoglobals + credentialManifestUniversityDegreeWithPresentationDefinition []byte //nolint:gochecknoglobals +) + +// Sample Credential Manifests for a driver's license. +var ( + //go:embed testdata/credential_manifest_drivers_license.json + credentialManifestDriversLicense []byte //nolint:gochecknoglobals + //go:embed testdata/credential_manifest_drivers_license_with_presentation_definition.json + credentialManifestDriversLicenseWithPresentationDefinition []byte //nolint:gochecknoglobals + //go:embed testdata/credential_manifest_drivers_license_with_presentation_definition_and_format.json + credentialManifestDriversLicenseWithPresentationDefinitionAndFormat []byte //nolint:gochecknoglobals +) + +// Sample verifiable credential for a university degree. +var ( //go:embed testdata/credential_university_degree.jsonld validVC []byte //nolint:gochecknoglobals ) @@ -37,17 +50,17 @@ const invalidJSONPath = "%InvalidJSONPath" func TestCredentialManifest_Unmarshal(t *testing.T) { t.Run("Valid Credential Manifest", func(t *testing.T) { t.Run("Without format or Presentation Submission", func(t *testing.T) { - makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) }) t.Run("With format", func(t *testing.T) { - makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) }) t.Run("With Presentation Submission", func(t *testing.T) { - makeCredentialManifestFromBytes(t, validCredentialManifestWithPresentationSubmission) + makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithPresentationDefinition) }) }) t.Run("Missing issuer ID", func(t *testing.T) { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.Issuer.ID = "" @@ -58,7 +71,7 @@ func TestCredentialManifest_Unmarshal(t *testing.T) { require.EqualError(t, err, "invalid credential manifest: issuer ID missing") }) t.Run("No output descriptors", func(t *testing.T) { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors = nil @@ -69,7 +82,7 @@ func TestCredentialManifest_Unmarshal(t *testing.T) { require.EqualError(t, err, "invalid credential manifest: no output descriptors found") }) t.Run("Output descriptor missing ID", func(t *testing.T) { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors[0].ID = "" @@ -80,7 +93,7 @@ func TestCredentialManifest_Unmarshal(t *testing.T) { require.EqualError(t, err, "invalid credential manifest: missing ID for output descriptor at index 0") }) t.Run("Duplicate output descriptor IDs", func(t *testing.T) { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors = append(credentialManifest.OutputDescriptors, @@ -94,7 +107,7 @@ func TestCredentialManifest_Unmarshal(t *testing.T) { "in multiple output descriptors") }) t.Run("Missing schema for output descriptor", func(t *testing.T) { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors[0].Schema = "" @@ -160,7 +173,7 @@ func TestCredentialManifest_ResolveOutputDescriptors(t *testing.T) { t.Run("All descriptors resolved without needing to use fallbacks", func(t *testing.T) { var credentialManifest cm.CredentialManifest - err := json.Unmarshal(validCredentialManifestBachelorsDegree, &credentialManifest) + err := json.Unmarshal(credentialManifestUniversityDegree, &credentialManifest) require.NoError(t, err) vc := parseTestCredential(t, validVC) @@ -179,7 +192,7 @@ func TestCredentialManifest_ResolveOutputDescriptors(t *testing.T) { t.Run("Fallbacks used for some descriptors", func(t *testing.T) { var credentialManifest cm.CredentialManifest - err := json.Unmarshal(validCredentialManifestBachelorsDegree, &credentialManifest) + err := json.Unmarshal(credentialManifestUniversityDegree, &credentialManifest) require.NoError(t, err) vc := parseTestCredential(t, createValidVCMissingSomeFields(t)) @@ -262,7 +275,7 @@ func createMarshalledCredentialManifestWithInvalidTitleJSONPath(t *testing.T) [] } func createCredentialManifestWithInvalidTitleJSONPath(t *testing.T) cm.CredentialManifest { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors[0].Display.Title.Paths[0] = invalidJSONPath @@ -279,7 +292,7 @@ func createMarshalledCredentialManifestWithInvalidSubtitleJSONPath(t *testing.T) } func createCredentialManifestWithInvalidSubtitleJSONPath(t *testing.T) cm.CredentialManifest { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors[0].Display.Subtitle.Paths[0] = invalidJSONPath @@ -296,7 +309,7 @@ func createMarshalledCredentialManifestWithInvalidDescriptionJSONPath(t *testing } func createCredentialManifestWithInvalidDescriptionJSONPath(t *testing.T) cm.CredentialManifest { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors[0].Display.Description.Paths = []string{invalidJSONPath} @@ -313,7 +326,7 @@ func createMarshalledCredentialManifestWithInvalidPropertyJSONPath(t *testing.T) } func createCredentialManifestWithInvalidPropertyJSONPath(t *testing.T) cm.CredentialManifest { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors[0].Display.Properties[0].Paths[0] = invalidJSONPath @@ -330,7 +343,7 @@ func createMarshalledCredentialManifestWithInvalidSchemaType(t *testing.T) []byt } func createCredentialManifestWithInvalidSchemaType(t *testing.T) cm.CredentialManifest { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors[0].Display.Title.Schema.Type = "InvalidSchemaType" @@ -347,7 +360,7 @@ func createMarshalledCredentialManifestWithInvalidSchemaFormat(t *testing.T) []b } func createCredentialManifestWithInvalidSchemaFormat(t *testing.T) cm.CredentialManifest { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestBachelorsDegree) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegree) credentialManifest.OutputDescriptors[0].Display.Title.Schema = cm.Schema{ Type: "string", @@ -358,7 +371,7 @@ func createCredentialManifestWithInvalidSchemaFormat(t *testing.T) cm.Credential } func createCredentialManifestWithNilJWTFormat(t *testing.T) cm.CredentialManifest { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) credentialManifest.Format.Jwt = nil @@ -366,7 +379,7 @@ func createCredentialManifestWithNilJWTFormat(t *testing.T) cm.CredentialManifes } func createCredentialManifestWithNilLDPFormat(t *testing.T) cm.CredentialManifest { - credentialManifest := makeCredentialManifestFromBytes(t, validCredentialManifestWithFormat) + credentialManifest := makeCredentialManifestFromBytes(t, credentialManifestUniversityDegreeWithFormat) credentialManifest.Format.Ldp = nil diff --git a/pkg/doc/cm/testdata/VP_minimal_with_credential_application.json b/pkg/doc/cm/testdata/VP_minimal_with_credential_application.json new file mode 100644 index 000000000..beaee0680 --- /dev/null +++ b/pkg/doc/cm/testdata/VP_minimal_with_credential_application.json @@ -0,0 +1,16 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/credential-manifest/application/v1" + ], + "credential_application": { + "id": "dc59653c-7e15-4718-80a4-46d6fb01ab7b", + "manifest_id": "dcc75a16-19f5-4273-84ce-4da69ee2b7fe", + "format": {} + }, + "type": [ + "VerifiablePresentation", + "CredentialApplication" + ], + "verifiableCredential": [] +} \ No newline at end of file diff --git a/pkg/doc/cm/testdata/VP_minimal_with_credential_application_and_presentation_submission.json b/pkg/doc/cm/testdata/VP_minimal_with_credential_application_and_presentation_submission.json new file mode 100644 index 000000000..00db11e96 --- /dev/null +++ b/pkg/doc/cm/testdata/VP_minimal_with_credential_application_and_presentation_submission.json @@ -0,0 +1,27 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/credential-manifest/application/v1" + ], + "credential_application": { + "id": "d30e40fb-4dc3-430b-ae85-7c409118903b", + "manifest_id": "dcc75a16-19f5-4273-84ce-4da69ee2b7fe", + "format": {} + }, + "presentation_submission": { + "id": "5c224f5e-3310-4ba6-8a8b-1eb694624110", + "definition_id": "8246867e-fdce-48de-a825-9d84ec16c6c9", + "descriptor_map": [ + { + "id": "prc_input", + "format": "ldp_vp", + "path": "$.verifiableCredential[0]" + } + ] + }, + "type": [ + "VerifiablePresentation", + "CredentialApplication" + ], + "verifiableCredential": [] +} \ No newline at end of file diff --git a/pkg/doc/cm/testdata/VP_minimal_with_credential_application_and_presentation_submission_and_format.json b/pkg/doc/cm/testdata/VP_minimal_with_credential_application_and_presentation_submission_and_format.json new file mode 100644 index 000000000..6e10eb697 --- /dev/null +++ b/pkg/doc/cm/testdata/VP_minimal_with_credential_application_and_presentation_submission_and_format.json @@ -0,0 +1,65 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/credential-manifest/application/v1" + ], + "credential_application": { + "id": "c4e3bffd-c20b-4588-9d00-21370f467bc9", + "manifest_id": "dcc75a16-19f5-4273-84ce-4da69ee2b7fe", + "format": { + "jwt": { + "alg": [ + "EdDSA", + "ES256K", + "ES384" + ] + }, + "jwt_vc": { + "alg": [ + "ES256K", + "ES384" + ] + }, + "jwt_vp": { + "alg": [ + "EdDSA", + "ES256K" + ] + }, + "ldp": { + "proof_type": [ + "RsaSignature2018" + ] + }, + "ldp_vc": { + "proof_type": [ + "JsonWebSignature2020", + "Ed25519Signature2018", + "EcdsaSecp256k1Signature2019", + "RsaSignature2018" + ] + }, + "ldp_vp": { + "proof_type": [ + "Ed25519Signature2018" + ] + } + } + }, + "presentation_submission": { + "id": "1ca2e521-3bbc-400b-bc0f-e4543bcf2801", + "definition_id": "8246867e-fdce-48de-a825-9d84ec16c6c9", + "descriptor_map": [ + { + "id": "prc_input", + "format": "ldp_vp", + "path": "$.verifiableCredential[0]" + } + ] + }, + "type": [ + "VerifiablePresentation", + "CredentialApplication" + ], + "verifiableCredential": [] +} \ No newline at end of file diff --git a/pkg/doc/cm/testdata/verifiable_presentation_basic_with_credential_fulfillment.json b/pkg/doc/cm/testdata/VP_minimal_with_credential_fulfillment.json similarity index 100% rename from pkg/doc/cm/testdata/verifiable_presentation_basic_with_credential_fulfillment.json rename to pkg/doc/cm/testdata/VP_minimal_with_credential_fulfillment.json diff --git a/pkg/doc/cm/testdata/VP_with_PR_Card_VC.json b/pkg/doc/cm/testdata/VP_with_PR_Card_VC.json new file mode 100644 index 000000000..74f602786 --- /dev/null +++ b/pkg/doc/cm/testdata/VP_with_PR_Card_VC.json @@ -0,0 +1,42 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1" + ], + "type": [ + "VerifiablePresentation" + ], + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1" + ], + "id": "urn:uvci:af5vshde843jf831j128fj", + "type": [ + "VaccinationCertificate", + "PermanentResidentCard" + ], + "name": "Permanent Resident Card", + "description": "Permanent Resident Card of Mr.Louis Pasteu", + "expirationDate": "2029-12-03T12:19:52Z", + "issuanceDate": "2019-12-03T12:19:52Z", + "issuer": "did:example:456", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "givenName": "Louis", + "familyName": "Pasteur", + "birthCountry": "Bahamas", + "birthDate": "1958-07-17" + }, + "proof": { + "type": "BbsBlsSignatureProof2020", + "created": "2021-02-18T23:04:28Z", + "nonce": "JNGovx4GGoi341v/YCTcZq7aLWtBtz8UhoxEeCxZFevEGzfh94WUSg8Ly/q+2jLqzzY=", + "proofPurpose": "assertionMethod", + "proofValue": "AB0GQA//jbDwMgaIIJeqP3fRyMYi6WDGhk0JlGJc/sk4ycuYGmyN7CbO4bA7yhIW/YQbHEkOgeMy0QM+usBgZad8x5FRePxfo4v1dSzAbJwWjx87G9F1lAIRgijlD4sYni1LhSo6svptDUmIrCAOwS2raV3G02mVejbwltMOo4+cyKcGlj9CzfjCgCuS1SqAxveDiMKGAAAAdJJF1pO6hBUGkebu/SMmiFafVdLvFgpMFUFEHTvElUQhwNSp6vxJp6Rs7pOVc9zHqAAAAAI7TJuDCf7ramzTo+syb7Njf6ExD11UKNcChaeblzegRBIkg3HoWgwR0hhd4z4D5/obSjGPKpGuD+1DoyTZhC/wqOjUZ03J1EtryZrC+y1DD14b4+khQVLgOBJ9+uvshrGDbu8+7anGezOa+qWT0FopAAAAEG6p07ghODpi8DVeDQyPwMY/iu2Lh7x3JShWniQrewY2GbsACBYOPlkNNm/qSExPRMe2X7UPpdsxpUDwqbObye4EXfAabgKd9gCmj2PNdvcOQAi5rIuJSGa4Vj7AtKoW/2vpmboPoOu4IEM1YviupomCKOzhjEuOof2/y5Adfb8JUVidWqf9Ye/HtxnzTu0HbaXL7jbwsMNn5wYfZuzpmVQgEXss2KePMSkHcfScAQNglnI90YgugHGuU+/DQcfMoA0+JviFcJy13yERAueVuzrDemzc+wJaEuNDn8UiTjAdVhLcgnHqUai+4F6ONbCfH2B3ohB3hSiGB6C7hDnEyXFOO9BijCTHrxPv3yKWNkks+3JfY28m+3NO0e2tlyH71yDX0+F6U388/bvWod/u5s3MpaCibTZEYoAc4sm4jW03HFYMmvYBuWOY6rGGOgIrXxQjx98D0macJJR7Hkh7KJhMkwvtyI4MaTPJsdJGfv8I+RFROxtRM7RcFpa4J5wF/wQnpyorqchwo6xAOKYFqCqKvI9B6Y7Da7/0iOiWsjs8a4zDiYynfYavnz6SdxCMpHLgplEQlnntqCb8C3qly2s5Ko3PGWu4M8Dlfcn4TT8YenkJDJicA91nlLaE8TJbBgsvgyT+zlTsRSXlFzQc+3KfWoODKZIZqTBaRZMft3S/", + "verificationMethod": "did:example:123#key-1" + } + } + ] +} \ No newline at end of file diff --git a/pkg/doc/cm/testdata/VP_with_PR_Card_VC_using_presentation_exchange_context.json b/pkg/doc/cm/testdata/VP_with_PR_Card_VC_using_presentation_exchange_context.json new file mode 100644 index 000000000..4fd2e5a61 --- /dev/null +++ b/pkg/doc/cm/testdata/VP_with_PR_Card_VC_using_presentation_exchange_context.json @@ -0,0 +1,43 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/presentation-exchange/submission/v1" + ], + "type": [ + "VerifiablePresentation" + ], + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1" + ], + "id": "urn:uvci:af5vshde843jf831j128fj", + "type": [ + "VaccinationCertificate", + "PermanentResidentCard" + ], + "name": "Permanent Resident Card", + "description": "Permanent Resident Card of Mr.Louis Pasteu", + "expirationDate": "2029-12-03T12:19:52Z", + "issuanceDate": "2019-12-03T12:19:52Z", + "issuer": "did:example:456", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "givenName": "Louis", + "familyName": "Pasteur", + "birthCountry": "Bahamas", + "birthDate": "1958-07-17" + }, + "proof": { + "type": "BbsBlsSignatureProof2020", + "created": "2021-02-18T23:04:28Z", + "nonce": "JNGovx4GGoi341v/YCTcZq7aLWtBtz8UhoxEeCxZFevEGzfh94WUSg8Ly/q+2jLqzzY=", + "proofPurpose": "assertionMethod", + "proofValue": "AB0GQA//jbDwMgaIIJeqP3fRyMYi6WDGhk0JlGJc/sk4ycuYGmyN7CbO4bA7yhIW/YQbHEkOgeMy0QM+usBgZad8x5FRePxfo4v1dSzAbJwWjx87G9F1lAIRgijlD4sYni1LhSo6svptDUmIrCAOwS2raV3G02mVejbwltMOo4+cyKcGlj9CzfjCgCuS1SqAxveDiMKGAAAAdJJF1pO6hBUGkebu/SMmiFafVdLvFgpMFUFEHTvElUQhwNSp6vxJp6Rs7pOVc9zHqAAAAAI7TJuDCf7ramzTo+syb7Njf6ExD11UKNcChaeblzegRBIkg3HoWgwR0hhd4z4D5/obSjGPKpGuD+1DoyTZhC/wqOjUZ03J1EtryZrC+y1DD14b4+khQVLgOBJ9+uvshrGDbu8+7anGezOa+qWT0FopAAAAEG6p07ghODpi8DVeDQyPwMY/iu2Lh7x3JShWniQrewY2GbsACBYOPlkNNm/qSExPRMe2X7UPpdsxpUDwqbObye4EXfAabgKd9gCmj2PNdvcOQAi5rIuJSGa4Vj7AtKoW/2vpmboPoOu4IEM1YviupomCKOzhjEuOof2/y5Adfb8JUVidWqf9Ye/HtxnzTu0HbaXL7jbwsMNn5wYfZuzpmVQgEXss2KePMSkHcfScAQNglnI90YgugHGuU+/DQcfMoA0+JviFcJy13yERAueVuzrDemzc+wJaEuNDn8UiTjAdVhLcgnHqUai+4F6ONbCfH2B3ohB3hSiGB6C7hDnEyXFOO9BijCTHrxPv3yKWNkks+3JfY28m+3NO0e2tlyH71yDX0+F6U388/bvWod/u5s3MpaCibTZEYoAc4sm4jW03HFYMmvYBuWOY6rGGOgIrXxQjx98D0macJJR7Hkh7KJhMkwvtyI4MaTPJsdJGfv8I+RFROxtRM7RcFpa4J5wF/wQnpyorqchwo6xAOKYFqCqKvI9B6Y7Da7/0iOiWsjs8a4zDiYynfYavnz6SdxCMpHLgplEQlnntqCb8C3qly2s5Ko3PGWu4M8Dlfcn4TT8YenkJDJicA91nlLaE8TJbBgsvgyT+zlTsRSXlFzQc+3KfWoODKZIZqTBaRZMft3S/", + "verificationMethod": "did:example:123#key-1" + } + } + ] +} \ No newline at end of file diff --git a/pkg/doc/cm/testdata/VP_with_PR_card_VC_and_credential_application.json b/pkg/doc/cm/testdata/VP_with_PR_card_VC_and_credential_application.json new file mode 100644 index 000000000..25ffdf047 --- /dev/null +++ b/pkg/doc/cm/testdata/VP_with_PR_card_VC_and_credential_application.json @@ -0,0 +1,49 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/credential-manifest/application/v1" + ], + "credential_application": { + "id": "d2c71762-0de5-4f8a-81c9-d21dffa8909b", + "manifest_id": "dcc75a16-19f5-4273-84ce-4da69ee2b7fe", + "format": {} + }, + "type": [ + "VerifiablePresentation", + "CredentialApplication" + ], + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1" + ], + "credentialSubject": { + "birthCountry": "Bahamas", + "birthDate": "1958-07-17", + "familyName": "Pasteur", + "givenName": "Louis", + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + }, + "description": "Permanent Resident Card of Mr.Louis Pasteu", + "expirationDate": "2029-12-03T12:19:52Z", + "id": "urn:uvci:af5vshde843jf831j128fj", + "issuanceDate": "2019-12-03T12:19:52Z", + "issuer": "did:example:456", + "name": "Permanent Resident Card", + "proof": { + "created": "2021-02-18T23:04:28Z", + "nonce": "JNGovx4GGoi341v/YCTcZq7aLWtBtz8UhoxEeCxZFevEGzfh94WUSg8Ly/q+2jLqzzY=", + "proofPurpose": "assertionMethod", + "proofValue": "AB0GQA//jbDwMgaIIJeqP3fRyMYi6WDGhk0JlGJc/sk4ycuYGmyN7CbO4bA7yhIW/YQbHEkOgeMy0QM+usBgZad8x5FRePxfo4v1dSzAbJwWjx87G9F1lAIRgijlD4sYni1LhSo6svptDUmIrCAOwS2raV3G02mVejbwltMOo4+cyKcGlj9CzfjCgCuS1SqAxveDiMKGAAAAdJJF1pO6hBUGkebu/SMmiFafVdLvFgpMFUFEHTvElUQhwNSp6vxJp6Rs7pOVc9zHqAAAAAI7TJuDCf7ramzTo+syb7Njf6ExD11UKNcChaeblzegRBIkg3HoWgwR0hhd4z4D5/obSjGPKpGuD+1DoyTZhC/wqOjUZ03J1EtryZrC+y1DD14b4+khQVLgOBJ9+uvshrGDbu8+7anGezOa+qWT0FopAAAAEG6p07ghODpi8DVeDQyPwMY/iu2Lh7x3JShWniQrewY2GbsACBYOPlkNNm/qSExPRMe2X7UPpdsxpUDwqbObye4EXfAabgKd9gCmj2PNdvcOQAi5rIuJSGa4Vj7AtKoW/2vpmboPoOu4IEM1YviupomCKOzhjEuOof2/y5Adfb8JUVidWqf9Ye/HtxnzTu0HbaXL7jbwsMNn5wYfZuzpmVQgEXss2KePMSkHcfScAQNglnI90YgugHGuU+/DQcfMoA0+JviFcJy13yERAueVuzrDemzc+wJaEuNDn8UiTjAdVhLcgnHqUai+4F6ONbCfH2B3ohB3hSiGB6C7hDnEyXFOO9BijCTHrxPv3yKWNkks+3JfY28m+3NO0e2tlyH71yDX0+F6U388/bvWod/u5s3MpaCibTZEYoAc4sm4jW03HFYMmvYBuWOY6rGGOgIrXxQjx98D0macJJR7Hkh7KJhMkwvtyI4MaTPJsdJGfv8I+RFROxtRM7RcFpa4J5wF/wQnpyorqchwo6xAOKYFqCqKvI9B6Y7Da7/0iOiWsjs8a4zDiYynfYavnz6SdxCMpHLgplEQlnntqCb8C3qly2s5Ko3PGWu4M8Dlfcn4TT8YenkJDJicA91nlLaE8TJbBgsvgyT+zlTsRSXlFzQc+3KfWoODKZIZqTBaRZMft3S/", + "type": "BbsBlsSignatureProof2020", + "verificationMethod": "did:example:123#key-1" + }, + "type": [ + "VaccinationCertificate", + "PermanentResidentCard" + ] + } + ] +} \ No newline at end of file diff --git a/pkg/doc/cm/testdata/VP_with_PR_card_VC_and_credential_application_and_presentation_submission.json b/pkg/doc/cm/testdata/VP_with_PR_card_VC_and_credential_application_and_presentation_submission.json new file mode 100644 index 000000000..010544d1a --- /dev/null +++ b/pkg/doc/cm/testdata/VP_with_PR_card_VC_and_credential_application_and_presentation_submission.json @@ -0,0 +1,60 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/credential-manifest/application/v1" + ], + "credential_application": { + "id": "f7553735-3a44-4bb6-9066-937ab606a3ea", + "manifest_id": "dcc75a16-19f5-4273-84ce-4da69ee2b7fe", + "format": {} + }, + "presentation_submission": { + "id": "9db9b308-4f49-4741-b571-6a1ca6b7bbe2", + "definition_id": "8246867e-fdce-48de-a825-9d84ec16c6c9", + "descriptor_map": [ + { + "id": "prc_input", + "format": "ldp_vp", + "path": "$.verifiableCredential[0]" + } + ] + }, + "type": [ + "VerifiablePresentation", + "CredentialApplication" + ], + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1" + ], + "credentialSubject": { + "birthCountry": "Bahamas", + "birthDate": "1958-07-17", + "familyName": "Pasteur", + "givenName": "Louis", + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + }, + "description": "Permanent Resident Card of Mr.Louis Pasteu", + "expirationDate": "2029-12-03T12:19:52Z", + "id": "urn:uvci:af5vshde843jf831j128fj", + "issuanceDate": "2019-12-03T12:19:52Z", + "issuer": "did:example:456", + "name": "Permanent Resident Card", + "proof": { + "created": "2021-02-18T23:04:28Z", + "nonce": "JNGovx4GGoi341v/YCTcZq7aLWtBtz8UhoxEeCxZFevEGzfh94WUSg8Ly/q+2jLqzzY=", + "proofPurpose": "assertionMethod", + "proofValue": "AB0GQA//jbDwMgaIIJeqP3fRyMYi6WDGhk0JlGJc/sk4ycuYGmyN7CbO4bA7yhIW/YQbHEkOgeMy0QM+usBgZad8x5FRePxfo4v1dSzAbJwWjx87G9F1lAIRgijlD4sYni1LhSo6svptDUmIrCAOwS2raV3G02mVejbwltMOo4+cyKcGlj9CzfjCgCuS1SqAxveDiMKGAAAAdJJF1pO6hBUGkebu/SMmiFafVdLvFgpMFUFEHTvElUQhwNSp6vxJp6Rs7pOVc9zHqAAAAAI7TJuDCf7ramzTo+syb7Njf6ExD11UKNcChaeblzegRBIkg3HoWgwR0hhd4z4D5/obSjGPKpGuD+1DoyTZhC/wqOjUZ03J1EtryZrC+y1DD14b4+khQVLgOBJ9+uvshrGDbu8+7anGezOa+qWT0FopAAAAEG6p07ghODpi8DVeDQyPwMY/iu2Lh7x3JShWniQrewY2GbsACBYOPlkNNm/qSExPRMe2X7UPpdsxpUDwqbObye4EXfAabgKd9gCmj2PNdvcOQAi5rIuJSGa4Vj7AtKoW/2vpmboPoOu4IEM1YviupomCKOzhjEuOof2/y5Adfb8JUVidWqf9Ye/HtxnzTu0HbaXL7jbwsMNn5wYfZuzpmVQgEXss2KePMSkHcfScAQNglnI90YgugHGuU+/DQcfMoA0+JviFcJy13yERAueVuzrDemzc+wJaEuNDn8UiTjAdVhLcgnHqUai+4F6ONbCfH2B3ohB3hSiGB6C7hDnEyXFOO9BijCTHrxPv3yKWNkks+3JfY28m+3NO0e2tlyH71yDX0+F6U388/bvWod/u5s3MpaCibTZEYoAc4sm4jW03HFYMmvYBuWOY6rGGOgIrXxQjx98D0macJJR7Hkh7KJhMkwvtyI4MaTPJsdJGfv8I+RFROxtRM7RcFpa4J5wF/wQnpyorqchwo6xAOKYFqCqKvI9B6Y7Da7/0iOiWsjs8a4zDiYynfYavnz6SdxCMpHLgplEQlnntqCb8C3qly2s5Ko3PGWu4M8Dlfcn4TT8YenkJDJicA91nlLaE8TJbBgsvgyT+zlTsRSXlFzQc+3KfWoODKZIZqTBaRZMft3S/", + "type": "BbsBlsSignatureProof2020", + "verificationMethod": "did:example:123#key-1" + }, + "type": [ + "VaccinationCertificate", + "PermanentResidentCard" + ] + } + ] +} \ No newline at end of file diff --git a/pkg/doc/cm/testdata/VP_with_PR_card_VC_and_credential_application_and_presentation_submission_and_format.json b/pkg/doc/cm/testdata/VP_with_PR_card_VC_and_credential_application_and_presentation_submission_and_format.json new file mode 100644 index 000000000..bfaa8dbcc --- /dev/null +++ b/pkg/doc/cm/testdata/VP_with_PR_card_VC_and_credential_application_and_presentation_submission_and_format.json @@ -0,0 +1,98 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://identity.foundation/credential-manifest/application/v1" + ], + "credential_application": { + "id": "8af3bb5d-b7d1-4c5b-b896-1c19ba84885e", + "manifest_id": "dcc75a16-19f5-4273-84ce-4da69ee2b7fe", + "format": { + "jwt": { + "alg": [ + "EdDSA", + "ES256K", + "ES384" + ] + }, + "jwt_vc": { + "alg": [ + "ES256K", + "ES384" + ] + }, + "jwt_vp": { + "alg": [ + "EdDSA", + "ES256K" + ] + }, + "ldp": { + "proof_type": [ + "RsaSignature2018" + ] + }, + "ldp_vc": { + "proof_type": [ + "JsonWebSignature2020", + "Ed25519Signature2018", + "EcdsaSecp256k1Signature2019", + "RsaSignature2018" + ] + }, + "ldp_vp": { + "proof_type": [ + "Ed25519Signature2018" + ] + } + } + }, + "presentation_submission": { + "id": "90c2bf01-bf4d-4aa5-8c61-e9e48366e890", + "definition_id": "8246867e-fdce-48de-a825-9d84ec16c6c9", + "descriptor_map": [ + { + "id": "prc_input", + "format": "ldp_vp", + "path": "$.verifiableCredential[0]" + } + ] + }, + "type": [ + "VerifiablePresentation", + "CredentialApplication" + ], + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/bbs/v1" + ], + "credentialSubject": { + "birthCountry": "Bahamas", + "birthDate": "1958-07-17", + "familyName": "Pasteur", + "givenName": "Louis", + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21" + }, + "description": "Permanent Resident Card of Mr.Louis Pasteu", + "expirationDate": "2029-12-03T12:19:52Z", + "id": "urn:uvci:af5vshde843jf831j128fj", + "issuanceDate": "2019-12-03T12:19:52Z", + "issuer": "did:example:456", + "name": "Permanent Resident Card", + "proof": { + "created": "2021-02-18T23:04:28Z", + "nonce": "JNGovx4GGoi341v/YCTcZq7aLWtBtz8UhoxEeCxZFevEGzfh94WUSg8Ly/q+2jLqzzY=", + "proofPurpose": "assertionMethod", + "proofValue": "AB0GQA//jbDwMgaIIJeqP3fRyMYi6WDGhk0JlGJc/sk4ycuYGmyN7CbO4bA7yhIW/YQbHEkOgeMy0QM+usBgZad8x5FRePxfo4v1dSzAbJwWjx87G9F1lAIRgijlD4sYni1LhSo6svptDUmIrCAOwS2raV3G02mVejbwltMOo4+cyKcGlj9CzfjCgCuS1SqAxveDiMKGAAAAdJJF1pO6hBUGkebu/SMmiFafVdLvFgpMFUFEHTvElUQhwNSp6vxJp6Rs7pOVc9zHqAAAAAI7TJuDCf7ramzTo+syb7Njf6ExD11UKNcChaeblzegRBIkg3HoWgwR0hhd4z4D5/obSjGPKpGuD+1DoyTZhC/wqOjUZ03J1EtryZrC+y1DD14b4+khQVLgOBJ9+uvshrGDbu8+7anGezOa+qWT0FopAAAAEG6p07ghODpi8DVeDQyPwMY/iu2Lh7x3JShWniQrewY2GbsACBYOPlkNNm/qSExPRMe2X7UPpdsxpUDwqbObye4EXfAabgKd9gCmj2PNdvcOQAi5rIuJSGa4Vj7AtKoW/2vpmboPoOu4IEM1YviupomCKOzhjEuOof2/y5Adfb8JUVidWqf9Ye/HtxnzTu0HbaXL7jbwsMNn5wYfZuzpmVQgEXss2KePMSkHcfScAQNglnI90YgugHGuU+/DQcfMoA0+JviFcJy13yERAueVuzrDemzc+wJaEuNDn8UiTjAdVhLcgnHqUai+4F6ONbCfH2B3ohB3hSiGB6C7hDnEyXFOO9BijCTHrxPv3yKWNkks+3JfY28m+3NO0e2tlyH71yDX0+F6U388/bvWod/u5s3MpaCibTZEYoAc4sm4jW03HFYMmvYBuWOY6rGGOgIrXxQjx98D0macJJR7Hkh7KJhMkwvtyI4MaTPJsdJGfv8I+RFROxtRM7RcFpa4J5wF/wQnpyorqchwo6xAOKYFqCqKvI9B6Y7Da7/0iOiWsjs8a4zDiYynfYavnz6SdxCMpHLgplEQlnntqCb8C3qly2s5Ko3PGWu4M8Dlfcn4TT8YenkJDJicA91nlLaE8TJbBgsvgyT+zlTsRSXlFzQc+3KfWoODKZIZqTBaRZMft3S/", + "type": "BbsBlsSignatureProof2020", + "verificationMethod": "did:example:123#key-1" + }, + "type": [ + "VaccinationCertificate", + "PermanentResidentCard" + ] + } + ] +} \ No newline at end of file diff --git a/pkg/doc/cm/testdata/verifiable_presentation_drivers_license_without_credential_fulfillment.json b/pkg/doc/cm/testdata/VP_with_drivers_license_VC.json similarity index 70% rename from pkg/doc/cm/testdata/verifiable_presentation_drivers_license_without_credential_fulfillment.json rename to pkg/doc/cm/testdata/VP_with_drivers_license_VC.json index d70297957..4aac54c83 100644 --- a/pkg/doc/cm/testdata/verifiable_presentation_drivers_license_without_credential_fulfillment.json +++ b/pkg/doc/cm/testdata/VP_with_drivers_license_VC.json @@ -29,12 +29,5 @@ "verificationMethod":"did:orb:EiA3Xmv8A8vUH5lRRZeKakd-cjAxGC2A4aoPDjLysjghow#tMIstfHSzXfBUF7O0m2FiBEfTb93_j_4ron47IXPgEo" } } - ], - "proof":{ - "created":"2021-06-07T20:02:44.730614315Z", - "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..NVum9BeYkhzwslZXm2cDOveQB9njlrCRSrdMZgwV3zZfLRXmZQ1AXdKLLmo4ClTYXFX_TWNyB8aFt9cN6sSvCg", - "proofPurpose":"authentication", - "type":"Ed25519Signature2018", - "verificationMethod":"did:orb:EiA3Xmv8A8vUH5lRRZeKakd-cjAxGC2A4aoPDjLysjghow#tMIstfHSzXfBUF7O0m2FiBEfTb93_j_4ron47IXPgEo" - } + ] } \ No newline at end of file diff --git a/pkg/doc/cm/testdata/verifiable_presentation_drivers_license_with_credential_fulfillment.json b/pkg/doc/cm/testdata/VP_with_drivers_license_VC_and_credential_fulfillment.json similarity index 76% rename from pkg/doc/cm/testdata/verifiable_presentation_drivers_license_with_credential_fulfillment.json rename to pkg/doc/cm/testdata/VP_with_drivers_license_VC_and_credential_fulfillment.json index 3309aca22..c98e7e353 100644 --- a/pkg/doc/cm/testdata/verifiable_presentation_drivers_license_with_credential_fulfillment.json +++ b/pkg/doc/cm/testdata/VP_with_drivers_license_VC_and_credential_fulfillment.json @@ -42,12 +42,5 @@ "verificationMethod":"did:orb:EiA3Xmv8A8vUH5lRRZeKakd-cjAxGC2A4aoPDjLysjghow#tMIstfHSzXfBUF7O0m2FiBEfTb93_j_4ron47IXPgEo" } } - ], - "proof":{ - "created":"2021-06-07T20:02:44.730614315Z", - "jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..NVum9BeYkhzwslZXm2cDOveQB9njlrCRSrdMZgwV3zZfLRXmZQ1AXdKLLmo4ClTYXFX_TWNyB8aFt9cN6sSvCg", - "proofPurpose":"authentication", - "type":"Ed25519Signature2018", - "verificationMethod":"did:orb:EiA3Xmv8A8vUH5lRRZeKakd-cjAxGC2A4aoPDjLysjghow#tMIstfHSzXfBUF7O0m2FiBEfTb93_j_4ron47IXPgEo" - } + ] } \ No newline at end of file diff --git a/pkg/doc/cm/testdata/credential_manifest_drivers_license.json b/pkg/doc/cm/testdata/credential_manifest_drivers_license.json index 31526e024..7db949b0f 100644 --- a/pkg/doc/cm/testdata/credential_manifest_drivers_license.json +++ b/pkg/doc/cm/testdata/credential_manifest_drivers_license.json @@ -8,86 +8,6 @@ } }, - "presentation_definition":{ - "id":"8246867e-fdce-48de-a825-9d84ec16c6c9", - "frame":{ - "@context":[ - "https://www.w3.org/2018/credentials/v1", - "https://w3id.org/citizenship/v1", - "https://w3id.org/security/suites/bls12381-2020/v1" - ], - "type":[ - "VerifiableCredential", - "PermanentResidentCard" - ], - "credentialSubject":{ - "@explicit":true, - "type":[ - "PermanentResident" - ], - "givenName":{ - - }, - "familyName":{ - - }, - "birthCountry":{ - - }, - "birthDate":{ - - } - } - }, - "input_descriptors":[ - { - "id":"prc_input", - "name":"Permanent Resident Card", - "purpose":"We need PRC to verify your status.", - "schema":[ - { - "uri": "https://w3id.org/citizenship#PermanentResidentCard" - } - ], - "constraints":{ - "fields":[ - { - "path":[ - "$.credentialSubject.givenName" - ], - "filter":{ - "type":"string" - } - }, - { - "path":[ - "$.credentialSubject.familyName" - ], - "filter":{ - "type":"string" - } - }, - { - "path":[ - "$.credentialSubject.birthCountry" - ], - "filter":{ - "type":"string" - } - }, - { - "path":[ - "$.credentialSubject.birthDate" - ], - "filter":{ - "type":"string" - } - } - ] - } - } - ] - }, "output_descriptors":[ { "id":"driver_license_output", diff --git a/pkg/doc/cm/testdata/credential_manifest_drivers_license_with_presentation_definition.json b/pkg/doc/cm/testdata/credential_manifest_drivers_license_with_presentation_definition.json new file mode 100644 index 000000000..31526e024 --- /dev/null +++ b/pkg/doc/cm/testdata/credential_manifest_drivers_license_with_presentation_definition.json @@ -0,0 +1,151 @@ +{ + "id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", + "version":"0.1.0", + "issuer":{ + "id":"did:example:123?linked-domains=3", + "name":"Washington State Government", + "styles":{ + + } + }, + "presentation_definition":{ + "id":"8246867e-fdce-48de-a825-9d84ec16c6c9", + "frame":{ + "@context":[ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/suites/bls12381-2020/v1" + ], + "type":[ + "VerifiableCredential", + "PermanentResidentCard" + ], + "credentialSubject":{ + "@explicit":true, + "type":[ + "PermanentResident" + ], + "givenName":{ + + }, + "familyName":{ + + }, + "birthCountry":{ + + }, + "birthDate":{ + + } + } + }, + "input_descriptors":[ + { + "id":"prc_input", + "name":"Permanent Resident Card", + "purpose":"We need PRC to verify your status.", + "schema":[ + { + "uri": "https://w3id.org/citizenship#PermanentResidentCard" + } + ], + "constraints":{ + "fields":[ + { + "path":[ + "$.credentialSubject.givenName" + ], + "filter":{ + "type":"string" + } + }, + { + "path":[ + "$.credentialSubject.familyName" + ], + "filter":{ + "type":"string" + } + }, + { + "path":[ + "$.credentialSubject.birthCountry" + ], + "filter":{ + "type":"string" + } + }, + { + "path":[ + "$.credentialSubject.birthDate" + ], + "filter":{ + "type":"string" + } + } + ] + } + } + ] + }, + "output_descriptors":[ + { + "id":"driver_license_output", + "schema":"https://schema.org/EducationalOccupationalCredential", + "display":{ + "title":{ + "path":[ + "$.name", + "$.vc.name" + ], + "schema": { + "type": "string" + }, + "fallback":"Washington State Driver License" + }, + "subtitle":{ + "path":[ + "$.class", + "$.vc.class" + ], + "schema": { + "type": "string" + }, + "fallback":"Class A, Commercial" + }, + "description":{ + "text":"License to operate a vehicle with a gross combined weight rating (GCWR) of 26,001 or more pounds, as long as the GVWR of the vehicle(s) being towed is over 10,000 pounds." + }, + "properties":[ + { + "path":[ + "$.donor", + "$.vc.donor" + ], + "schema": { + "type": "boolean" + }, + "fallback":"Unknown", + "label":"Organ Donor" + } + ] + }, + "styles":{ + "thumbnail":{ + "uri":"https://dol.wa.com/logo.png", + "alt":"Washington State Seal" + }, + "hero":{ + "uri":"https://dol.wa.com/happy-people-driving.png", + "alt":"Happy people driving" + }, + "background":{ + "color":"#ff0000" + }, + "text":{ + "color":"#d4d400" + } + } + } + ] +} \ No newline at end of file diff --git a/pkg/doc/cm/testdata/credential_manifest_drivers_license_with_presentation_definition_and_format.json b/pkg/doc/cm/testdata/credential_manifest_drivers_license_with_presentation_definition_and_format.json new file mode 100644 index 000000000..d978ef953 --- /dev/null +++ b/pkg/doc/cm/testdata/credential_manifest_drivers_license_with_presentation_definition_and_format.json @@ -0,0 +1,176 @@ +{ + "id":"dcc75a16-19f5-4273-84ce-4da69ee2b7fe", + "version":"0.1.0", + "issuer":{ + "id":"did:example:123?linked-domains=3", + "name":"Washington State Government", + "styles":{ + + } + }, + "presentation_definition":{ + "id":"8246867e-fdce-48de-a825-9d84ec16c6c9", + "frame":{ + "@context":[ + "https://www.w3.org/2018/credentials/v1", + "https://w3id.org/citizenship/v1", + "https://w3id.org/security/suites/bls12381-2020/v1" + ], + "type":[ + "VerifiableCredential", + "PermanentResidentCard" + ], + "credentialSubject":{ + "@explicit":true, + "type":[ + "PermanentResident" + ], + "givenName":{ + + }, + "familyName":{ + + }, + "birthCountry":{ + + }, + "birthDate":{ + + } + } + }, + "input_descriptors":[ + { + "id":"prc_input", + "name":"Permanent Resident Card", + "purpose":"We need PRC to verify your status.", + "schema":[ + { + "uri": "https://w3id.org/citizenship#PermanentResidentCard" + } + ], + "constraints":{ + "fields":[ + { + "path":[ + "$.credentialSubject.givenName" + ], + "filter":{ + "type":"string" + } + }, + { + "path":[ + "$.credentialSubject.familyName" + ], + "filter":{ + "type":"string" + } + }, + { + "path":[ + "$.credentialSubject.birthCountry" + ], + "filter":{ + "type":"string" + } + }, + { + "path":[ + "$.credentialSubject.birthDate" + ], + "filter":{ + "type":"string" + } + } + ] + } + } + ] + }, + "output_descriptors":[ + { + "id":"driver_license_output", + "schema":"https://schema.org/EducationalOccupationalCredential", + "display":{ + "title":{ + "path":[ + "$.name", + "$.vc.name" + ], + "schema": { + "type": "string" + }, + "fallback":"Washington State Driver License" + }, + "subtitle":{ + "path":[ + "$.class", + "$.vc.class" + ], + "schema": { + "type": "string" + }, + "fallback":"Class A, Commercial" + }, + "description":{ + "text":"License to operate a vehicle with a gross combined weight rating (GCWR) of 26,001 or more pounds, as long as the GVWR of the vehicle(s) being towed is over 10,000 pounds." + }, + "properties":[ + { + "path":[ + "$.donor", + "$.vc.donor" + ], + "schema": { + "type": "boolean" + }, + "fallback":"Unknown", + "label":"Organ Donor" + } + ] + }, + "styles":{ + "thumbnail":{ + "uri":"https://dol.wa.com/logo.png", + "alt":"Washington State Seal" + }, + "hero":{ + "uri":"https://dol.wa.com/happy-people-driving.png", + "alt":"Happy people driving" + }, + "background":{ + "color":"#ff0000" + }, + "text":{ + "color":"#d4d400" + } + } + } + ], + "format": { + "jwt": { + "alg": ["EdDSA", "ES256K", "ES384"] + }, + "jwt_vc": { + "alg": ["ES256K", "ES384"] + }, + "jwt_vp": { + "alg": ["EdDSA", "ES256K"] + }, + "ldp": { + "proof_type": ["RsaSignature2018"] + }, + "ldp_vc": { + "proof_type": [ + "JsonWebSignature2020", + "Ed25519Signature2018", + "EcdsaSecp256k1Signature2019", + "RsaSignature2018" + ] + }, + "ldp_vp": { + "proof_type": ["Ed25519Signature2018"] + } + } +} \ No newline at end of file diff --git a/pkg/doc/ldcontext/embed/embed_contexts.go b/pkg/doc/ldcontext/embed/embed_contexts.go index 39af133a3..692b31c5a 100644 --- a/pkg/doc/ldcontext/embed/embed_contexts.go +++ b/pkg/doc/ldcontext/embed/embed_contexts.go @@ -44,6 +44,8 @@ var ( secp256k12019 []byte //go:embed third_party/identity.foundation/credential-fulfillment.jsonld credentialFulfillment []byte + //go:embed third_party/identity.foundation/credential-application.jsonld + credentialApplication []byte ) // Contexts contains JSON-LD contexts embedded into a Go binary. @@ -133,4 +135,9 @@ var Contexts = []ldcontext.Document{ //nolint:gochecknoglobals DocumentURL: "https://identity.foundation/credential-manifest/fulfillment/v1", Content: credentialFulfillment, }, + { + URL: "https://identity.foundation/credential-manifest/application/v1", + DocumentURL: "https://identity.foundation/credential-manifest/application/v1", + Content: credentialApplication, + }, } diff --git a/pkg/doc/ldcontext/embed/third_party/identity.foundation/credential-application.jsonld b/pkg/doc/ldcontext/embed/third_party/identity.foundation/credential-application.jsonld new file mode 100644 index 000000000..e2f984b7b --- /dev/null +++ b/pkg/doc/ldcontext/embed/third_party/identity.foundation/credential-application.jsonld @@ -0,0 +1,19 @@ +{ + "@context": { + "@version": 1.1, + "CredentialApplication": { + "@id": "https://identity.foundation/credential-manifest/#credential-application", + "@context": { + "@version": 1.1, + "credential_application": { + "@id": "https://identity.foundation/credential-manifest/#credential-application", + "@type": "@json" + }, + "presentation_submission": { + "@id": "https://identity.foundation/presentation-exchange/#presentation-submission", + "@type": "@json" + } + } + } + } +} \ No newline at end of file