diff --git a/didconfig/verifier/didconfig_test.go b/didconfig/verifier/didconfig_test.go index 7679145..8492af2 100644 --- a/didconfig/verifier/didconfig_test.go +++ b/didconfig/verifier/didconfig_test.go @@ -200,7 +200,7 @@ func TestIsValidDomainCredentialJWT(t *testing.T) { dlcJWT, err := verifiable.CreateCredential(verifiable.CredentialContents{ Issued: afgotime.NewTime(time.Now()), Expired: afgotime.NewTime(time.Now().Add(time.Hour)), - Context: []string{verifiable.ContextURI, ContextV1}, + Context: []string{verifiable.V1ContextURI, ContextV1}, Types: []string{verifiable.VCType, domainLinkageCredentialType}, Subject: []verifiable.Subject{{ID: testDID, CustomFields: map[string]interface{}{"origin": testJWTDomain}}}, Issuer: &verifiable.Issuer{ID: testDID}, @@ -241,7 +241,7 @@ func TestIsValidDomainCredentialJWT(t *testing.T) { dlcJWT, err := verifiable.CreateCredential(verifiable.CredentialContents{ Issued: afgotime.NewTime(time.Now()), Expired: afgotime.NewTime(time.Now().Add(time.Hour)), - Context: []string{verifiable.ContextURI, ContextV1}, + Context: []string{verifiable.V1ContextURI, ContextV1}, Types: []string{verifiable.VCType, domainLinkageCredentialType}, Subject: []verifiable.Subject{{ID: testDID, CustomFields: map[string]interface{}{"origin": testJWTDomain}}}, Issuer: &verifiable.Issuer{ID: testDID}, @@ -286,7 +286,7 @@ func TestIsValidDomainCredentialJWT(t *testing.T) { dlcJWT, err := verifiable.CreateCredential(verifiable.CredentialContents{ Issued: afgotime.NewTime(time.Now()), Expired: afgotime.NewTime(time.Now().Add(time.Hour)), - Context: []string{verifiable.ContextURI, ContextV1}, + Context: []string{verifiable.V1ContextURI, ContextV1}, Types: []string{verifiable.VCType, domainLinkageCredentialType}, Subject: []verifiable.Subject{{ID: testDID, CustomFields: map[string]interface{}{"origin": testJWTDomain}}}, Issuer: &verifiable.Issuer{ID: testDID}, @@ -330,7 +330,7 @@ func TestIsValidDomainCredentialJWT(t *testing.T) { dlcJWT, err := verifiable.CreateCredential(verifiable.CredentialContents{ Issued: afgotime.NewTime(time.Now()), Expired: afgotime.NewTime(time.Now().Add(time.Hour)), - Context: []string{verifiable.ContextURI, ContextV1}, + Context: []string{verifiable.V1ContextURI, ContextV1}, Types: []string{verifiable.VCType, domainLinkageCredentialType}, Subject: []verifiable.Subject{{ID: testDID, CustomFields: map[string]interface{}{"origin": testJWTDomain}}}, Issuer: &verifiable.Issuer{ID: testDID}, @@ -365,7 +365,7 @@ func TestIsValidDomainCredentialJWT(t *testing.T) { dlcJWT, err := verifiable.CreateCredential(verifiable.CredentialContents{ Issued: afgotime.NewTime(time.Now()), Expired: afgotime.NewTime(time.Now().Add(time.Hour)), - Context: []string{verifiable.ContextURI, ContextV1}, + Context: []string{verifiable.V1ContextURI, ContextV1}, Types: []string{verifiable.VCType, domainLinkageCredentialType}, Subject: []verifiable.Subject{{ID: testDID, CustomFields: map[string]interface{}{"origin": testJWTDomain}}}, Issuer: &verifiable.Issuer{ID: testDID}, @@ -440,7 +440,7 @@ func TestIsValidDomainCredentialJWT(t *testing.T) { dlcJWT, err := verifiable.CreateCredential(verifiable.CredentialContents{ Issued: afgotime.NewTime(time.Now()), Expired: afgotime.NewTime(time.Now().Add(time.Hour)), - Context: []string{verifiable.ContextURI, ContextV1}, + Context: []string{verifiable.V1ContextURI, ContextV1}, Types: []string{verifiable.VCType, domainLinkageCredentialType}, Subject: []verifiable.Subject{{ID: "did:key:different", CustomFields: map[string]interface{}{"origin": testJWTDomain}}}, // nolint:lll Issuer: &verifiable.Issuer{ID: testDID}, @@ -461,7 +461,7 @@ func TestIsValidDomainCredentialJWT(t *testing.T) { dlcJWT, err := verifiable.CreateCredential(verifiable.CredentialContents{ Issued: afgotime.NewTime(time.Now()), Expired: afgotime.NewTime(time.Now().Add(time.Hour)), - Context: []string{verifiable.ContextURI, ContextV1}, + Context: []string{verifiable.V1ContextURI, ContextV1}, Types: []string{verifiable.VCType, domainLinkageCredentialType}, Subject: []verifiable.Subject{{ID: testDID, CustomFields: map[string]interface{}{"origin": testJWTDomain}}}, Issuer: &verifiable.Issuer{ID: testDID}, @@ -472,7 +472,9 @@ func TestIsValidDomainCredentialJWT(t *testing.T) { dlcJWT = createEdDSAJWS(t, dlcJWT, ed25519ProofCreator, testKID, false) - borkenJWT, err := verifiable.CreateCredential(verifiable.CredentialContents{}, nil) + borkenJWT, err := verifiable.CreateCredential(verifiable.CredentialContents{ + Context: []string{verifiable.V1ContextURI}, + }, nil) require.NoError(t, err) borkenJWT.JWTEnvelope = dlcJWT.JWTEnvelope diff --git a/go.mod b/go.mod index d8020eb..0dadd30 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/tidwall/gjson v1.14.3 github.com/tidwall/sjson v1.1.4 github.com/trustbloc/bbs-signature-go v1.0.2 - github.com/trustbloc/did-go v1.3.0 + github.com/trustbloc/did-go v1.3.1-0.20240910130808-bf0188fdfe70 github.com/trustbloc/kms-go v1.1.2 github.com/veraison/go-cose v1.1.1-0.20240126165338-2300d5c96dbd github.com/xeipuuv/gojsonschema v1.2.0 diff --git a/go.sum b/go.sum index fdd7a9b..78109cc 100644 --- a/go.sum +++ b/go.sum @@ -132,8 +132,8 @@ github.com/tidwall/sjson v1.1.4 h1:bTSsPLdAYF5QNLSwYsKfBKKTnlGbIuhqL3CpRsjzGhg= github.com/tidwall/sjson v1.1.4/go.mod h1:wXpKXu8CtDjKAZ+3DrKY5ROCorDFahq8l0tey/Lx1fg= github.com/trustbloc/bbs-signature-go v1.0.2 h1:gepEsbLiZHv/vva9FKG5gF38mGtOIyGez7desZxiI1o= github.com/trustbloc/bbs-signature-go v1.0.2/go.mod h1:xYotcXHAbcE0TO+SteW0J6XI3geQaXq4wdnXR2k+XCU= -github.com/trustbloc/did-go v1.3.0 h1:vfiY/dewd9cORo3U5c+eoWgNIQnKsuQYn5PV0CRu1o0= -github.com/trustbloc/did-go v1.3.0/go.mod h1:packTRoBoo8DrwOE7QKsI98xXS3Vf6ovUXYD4FUAcB4= +github.com/trustbloc/did-go v1.3.1-0.20240910130808-bf0188fdfe70 h1:5GOoXZcKYDTsobaTCeFUEBjbognYbjBh38fCjkvClU4= +github.com/trustbloc/did-go v1.3.1-0.20240910130808-bf0188fdfe70/go.mod h1:packTRoBoo8DrwOE7QKsI98xXS3Vf6ovUXYD4FUAcB4= github.com/trustbloc/kms-go v1.1.2 h1:nAlhDoHkSyX1eQFRz/sJsdgmJuNadyX7FJEy/9ROwys= github.com/trustbloc/kms-go v1.1.2/go.mod h1:OKOtsLbE6W5s4mpjWkvk8XEqcmt9vTgVmDNkHELpWO0= github.com/veraison/go-cose v1.1.1-0.20240126165338-2300d5c96dbd h1:QhdCHSW1/oosJbzBTEYLU6xcKxXbQzzqFnhCtW2UWbA= diff --git a/presexch/definition_test.go b/presexch/definition_test.go index fb7acf3..f860fa5 100644 --- a/presexch/definition_test.go +++ b/presexch/definition_test.go @@ -73,7 +73,7 @@ func TestPresentationDefinition_IsValid(t *testing.T) { }) } -func TestPresentationDefinition_CreateVP(t *testing.T) { +func TestPresentationDefinition_CreateVP_V1Credential(t *testing.T) { lddl := createTestJSONLDDocumentLoader(t) t.Run("Checks schema", func(t *testing.T) { @@ -85,12 +85,12 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { require.Nil(t, vp) }) - t.Run("Checks submission requirements", func(t *testing.T) { + t.Run("Checks credentials V1 submission requirements", func(t *testing.T) { issuerID := "did:example:76e12ec712ebc6f1c221ebfeb1f" vc1 := createTestCredential(t, credentialProto{ Issued: utiltime.NewTime(time.Now()), - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: "http://example.edu/credentials/1872", Subject: []verifiable.Subject{{ID: issuerID}}, @@ -114,7 +114,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { candidateVCs := []*verifiable.Credential{ vc1JWT, createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: "http://example.edu/credentials/1872", CustomFields: map[string]interface{}{ @@ -123,7 +123,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { Proofs: []verifiable.Proof{{"type": "JsonWebSignature2020"}}, }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: "http://example.edu/credentials/1872", Subject: []verifiable.Subject{{ID: issuerID}}, @@ -136,7 +136,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { Proofs: []verifiable.Proof{{"type": "JsonWebSignature2020"}}, }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: "http://example.edu/credentials/1872", Subject: []verifiable.Subject{{ID: issuerID}}, @@ -232,7 +232,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { ID: uuid.New().String(), Group: []string{"A"}, Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -244,7 +244,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { ID: uuid.New().String(), Group: []string{"child"}, Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -261,7 +261,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { ID: uuid.New().String(), Group: []string{"teenager"}, Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -278,7 +278,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { ID: uuid.New().String(), Group: []string{"adult"}, Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -344,7 +344,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { ID: uuid.New().String(), Group: []string{"A"}, Schema: []*Schema{{ - URI: verifiable.ContextURI, + URI: verifiable.V1ContextURI, }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -357,13 +357,14 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), CustomFields: map[string]interface{}{ "first_name": "Jesse", }, }), createTestCredential(t, credentialProto{ + Context: []string{verifiable.V1ContextURI}, ID: uuid.New().String(), Subject: []verifiable.Subject{{ID: issuerID}}, Issuer: &verifiable.Issuer{ID: issuerID}, @@ -396,7 +397,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { ID: "get_" + claim, Group: groups, Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -416,7 +417,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { } vc := createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: "https://example.com/credential/" + uuid.NewString(), Subject: []verifiable.Subject{{ID: selfIssuedID}}, @@ -462,7 +463,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -476,7 +477,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: "http://example.edu/credentials/1872", Subject: []verifiable.Subject{{ID: "did:example:76e12ec712ebc6f1c221ebfeb1f"}}, @@ -540,7 +541,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType, "DemoCred"}, ID: "http://example.edu/credentials/1872", Subject: []verifiable.Subject{{ID: "did:example:76e12ec712ebc6f1c221ebfeb1f"}}, @@ -574,7 +575,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -589,7 +590,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: "http://example.edu/credentials/1872", Subject: []verifiable.Subject{{ID: "did:example:76e12ec712ebc6f1c221ebfeb1f"}}, @@ -632,7 +633,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -646,7 +647,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { } credProto := createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: "http://example.edu/credentials/1872", Subject: []verifiable.Subject{{ID: "did:example:76e12ec712ebc6f1c221ebfeb1f"}}, @@ -712,7 +713,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -771,7 +772,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -840,7 +841,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -894,7 +895,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -988,7 +989,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -1021,7 +1022,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -1058,7 +1059,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1094,7 +1095,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -1126,7 +1127,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { ID: uuid.New().String(), InputDescriptors: []*InputDescriptor{{ Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, ID: uuid.New().String(), Constraints: &Constraints{ @@ -1142,7 +1143,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vc := createTestCredential(t, credentialProto{ ID: "https://issuer.oidp.uscis.gov/credentials/83627465", Context: []string{ - verifiable.ContextURI, + verifiable.V1ContextURI, "https://www.w3.org/2018/credentials/examples/v1", "https://w3id.org/security/bbs/v1", }, @@ -1238,7 +1239,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { ID: uuid.New().String(), InputDescriptors: []*InputDescriptor{{ Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, ID: uuid.New().String(), Constraints: &Constraints{ @@ -1258,7 +1259,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vc := createTestCredential(t, credentialProto{ ID: "https://issuer.oidp.uscis.gov/credentials/83627465", Context: []string{ - verifiable.ContextURI, + verifiable.V1ContextURI, "https://w3id.org/citizenship/v1", "https://w3id.org/security/bbs/v1", }, @@ -1346,7 +1347,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: verifiable.ContextID, + URI: verifiable.V1ContextID, }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1359,7 +1360,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), CustomFields: map[string]interface{}{ @@ -1379,7 +1380,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: verifiable.ContextID, + URI: verifiable.V1ContextID, }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1391,7 +1392,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), CustomFields: map[string]interface{}{ @@ -1399,7 +1400,8 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }, }), createTestCredential(t, credentialProto{ - ID: uuid.New().String(), + Context: []string{verifiable.V1ContextURI}, + ID: uuid.New().String(), CustomFields: map[string]interface{}{ "last_name": "Travis", }, @@ -1416,7 +1418,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: verifiable.ContextID, + URI: verifiable.V1ContextID, }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1430,7 +1432,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), CustomFields: map[string]interface{}{ @@ -1438,7 +1440,8 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }, }), createTestCredential(t, credentialProto{ - ID: uuid.New().String(), + Context: []string{verifiable.V1ContextURI}, + ID: uuid.New().String(), CustomFields: map[string]interface{}{ "last_name": "Travis", }, @@ -1457,7 +1460,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -1474,7 +1477,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Subject: []verifiable.Subject{}, @@ -1482,7 +1485,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { "first_name": "Jesse", }, }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Subject: []verifiable.Subject{{ID: issuerID}}, @@ -1511,7 +1514,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -1534,7 +1537,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Subject: []verifiable.Subject{{}}, @@ -1545,7 +1548,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }), createTestCredential(t, credentialProto{ ID: "http://example.edu/credentials/1872", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{"VerifiableCredential"}, Subject: []verifiable.Subject{{ID: issuerID}}, Issuer: &verifiable.Issuer{ID: issuerID}, @@ -1621,7 +1624,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -1639,7 +1642,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Issuer: &verifiable.Issuer{CustomFields: map[string]interface{}{"k": "v"}}, @@ -1662,7 +1665,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -1679,7 +1682,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Subject: []verifiable.Subject{{ID: issuerID}}, @@ -1689,7 +1692,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }, }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Subject: []verifiable.Subject{{ID: "123"}}, @@ -1716,7 +1719,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -1729,7 +1732,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), CustomFields: map[string]interface{}{ @@ -1737,7 +1740,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }, }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Subject: []verifiable.Subject{{ID: issuerID}}, @@ -1765,7 +1768,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -1778,7 +1781,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }, { ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1792,7 +1795,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Subject: []verifiable.Subject{{ID: issuerID}}, @@ -1802,7 +1805,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }, }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Subject: []verifiable.Subject{{ID: issuerID}}, @@ -1828,7 +1831,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1840,7 +1843,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), CustomFields: map[string]interface{}{ @@ -1848,7 +1851,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }, }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), CustomFields: map[string]interface{}{ @@ -1872,19 +1875,19 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, }}, } vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), }), @@ -1911,12 +1914,12 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, + Context: []string{verifiable.V1ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, Types: []string{verifiable.VCType, "UniversityDegreeCredential"}, ID: uuid.New().String(), }), @@ -1943,7 +1946,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Schemas: []verifiable.TypedID{{ @@ -1952,7 +1955,8 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }}, }), createTestCredential(t, credentialProto{ - ID: uuid.New().String(), + Context: []string{verifiable.V1ContextURI}, + ID: uuid.New().String(), Schemas: []verifiable.TypedID{{ ID: "https://www.w3.org/TR/vc-data-model/3.0/#types", Type: "JsonSchemaValidator2018", @@ -1982,12 +1986,12 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, + Context: []string{verifiable.V1ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, Types: []string{verifiable.VCType, "UniversityDegreeCredential"}, ID: uuid.New().String(), }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, + Context: []string{verifiable.V1ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, Types: []string{verifiable.VCType, "DocumentVerification"}, ID: uuid.New().String(), }), @@ -2019,7 +2023,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Schemas: []verifiable.TypedID{{ @@ -2028,7 +2032,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }}, }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Schemas: []verifiable.TypedID{{ @@ -2062,7 +2066,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { } vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Schemas: []verifiable.TypedID{{ @@ -2071,7 +2075,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }}, }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Schemas: []verifiable.TypedID{{ @@ -2097,7 +2101,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { } vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), }), @@ -2124,19 +2128,19 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { Schema: []*Schema{{ URI: "https://www.w3.org/TR/vc-data-model/2.0/#types", }, { - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), Required: true, }}, }}, } vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, + Context: []string{verifiable.V1ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, Types: []string{verifiable.VCType, "DocumentVerification"}, ID: uuid.New().String(), }), @@ -2156,7 +2160,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { InputDescriptors: []*InputDescriptor{{ ID: uuid.New().String(), Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, }, { ID: uuid.New().String(), @@ -2171,7 +2175,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { } vp, err := pd.CreateVP([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), Schemas: []verifiable.TypedID{{ @@ -2181,7 +2185,7 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { }), createTestCredential(t, credentialProto{ Context: []string{ - verifiable.ContextURI, + verifiable.V1ContextURI, "https://www.w3.org/2018/credentials/examples/v1", "https://trustbloc.github.io/context/vc/examples-v1.jsonld", }, @@ -2217,12 +2221,12 @@ func TestPresentationDefinition_CreateVP(t *testing.T) { vp, err := pd.CreateVP( []*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, + Context: []string{verifiable.V1ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, Types: []string{verifiable.VCType, "UniversityDegreeCredential"}, ID: uuid.New().String(), }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, + Context: []string{verifiable.V1ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, Types: []string{verifiable.VCType, "DocumentVerification"}, ID: uuid.New().String(), }), @@ -2276,12 +2280,12 @@ func TestPresentationDefinition_CreateVPArray(t *testing.T) { vpList, ps, err := pd.CreateVPArray( []*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, + Context: []string{verifiable.V1ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, Types: []string{verifiable.VCType, "UniversityDegreeCredential"}, ID: uuid.New().String(), }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, + Context: []string{verifiable.V1ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, Types: []string{verifiable.VCType, "DocumentVerification"}, ID: uuid.New().String(), }), @@ -2321,12 +2325,12 @@ func TestPresentationDefinition_CreateVPArray(t *testing.T) { vpList, ps, err := pd.CreateVPArray( []*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, + Context: []string{verifiable.V1ContextURI, "https://www.w3.org/2018/credentials/examples/v1"}, Types: []string{verifiable.VCType, "UniversityDegreeCredential"}, ID: uuid.New().String(), }), createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, + Context: []string{verifiable.V1ContextURI, "https://trustbloc.github.io/context/vc/examples-v1.jsonld"}, Types: []string{verifiable.VCType, "DocumentVerification"}, ID: uuid.New().String(), }), @@ -2385,10 +2389,9 @@ func TestExtractExtraFields(t *testing.T) { }, }) - require.EqualValues(t, []string{ - "mhV9Kt70m-8slbu1TgIpdr6_AWO-kG51Q2amF3w9qQyyxM-aXsTn77uxMBAnFM67", - "xxx", - }, results) + require.Len(t, results, 2) + require.Contains(t, results, "mhV9Kt70m-8slbu1TgIpdr6_AWO-kG51Q2amF3w9qQyyxM-aXsTn77uxMBAnFM67") + require.Contains(t, results, "xxx") } func getTestVCWithContext(t *testing.T, issuerID string, ctx []string) *verifiable.Credential { @@ -2411,7 +2414,7 @@ func getTestVCWithContext(t *testing.T, issuerID string, ctx []string) *verifiab subject, err := verifiable.SubjectFromJSON(subjectJSON) require.NoError(t, err) - context := []string{verifiable.ContextURI} + context := []string{verifiable.V1ContextURI} if ctx != nil { context = append(context, ctx...) diff --git a/presexch/example_v1_test.go b/presexch/example_v1_test.go index 17f833f..ede5c34 100644 --- a/presexch/example_v1_test.go +++ b/presexch/example_v1_test.go @@ -33,7 +33,7 @@ func ExamplePresentationDefinition_CreateVP_v1() { ID: "age_descriptor", Purpose: "Your age should be greater or equal to 18.", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -57,7 +57,7 @@ func ExamplePresentationDefinition_CreateVP_v1() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", @@ -139,7 +139,7 @@ func ExamplePresentationDefinition_CreateVP_v1_With_LDP_FormatAndProof() { ID: "age_descriptor", Purpose: "Your age should be greater or equal to 18.", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -166,7 +166,7 @@ func ExamplePresentationDefinition_CreateVP_v1_With_LDP_FormatAndProof() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", @@ -249,7 +249,7 @@ func ExamplePresentationDefinition_CreateVP_v1_With_LDPVC_FormatAndProof() { ID: "age_descriptor", Purpose: "Your age should be greater or equal to 18.", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -276,7 +276,7 @@ func ExamplePresentationDefinition_CreateVP_v1_With_LDPVC_FormatAndProof() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", @@ -357,7 +357,7 @@ func ExamplePresentationDefinition_CreateVP_multipleMatches() { ID: "age_descriptor", Purpose: "Your age should be greater or equal to 18.", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -372,7 +372,7 @@ func ExamplePresentationDefinition_CreateVP_multipleMatches() { ID: "first_name_descriptor", Purpose: "First name must be either Andrew or Jesse", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -394,7 +394,7 @@ func ExamplePresentationDefinition_CreateVP_multipleMatches() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:777", @@ -411,7 +411,7 @@ func ExamplePresentationDefinition_CreateVP_multipleMatches() { }, { ID: "http://example.edu/credentials/888", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:888", @@ -542,7 +542,7 @@ func ExamplePresentationDefinition_CreateVP_multipleMatchesDisclosure() { ID: "age_descriptor", Purpose: "Your age should be greater or equal to 18.", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -557,7 +557,7 @@ func ExamplePresentationDefinition_CreateVP_multipleMatchesDisclosure() { ID: "first_name_descriptor", Purpose: "First name must be either Andrew or Jesse", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -580,7 +580,7 @@ func ExamplePresentationDefinition_CreateVP_multipleMatchesDisclosure() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:777", @@ -597,7 +597,7 @@ func ExamplePresentationDefinition_CreateVP_multipleMatchesDisclosure() { }, { ID: "http://example.edu/credentials/888", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:888", @@ -772,7 +772,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirementsLimitDisclosur Group: []string{"A"}, Purpose: "Your age should be greater or equal to 18.", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -788,7 +788,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirementsLimitDisclosur Group: []string{"drivers_license_image"}, Purpose: "We need your photo to identify you", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -805,7 +805,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirementsLimitDisclosur Group: []string{"passport_image"}, Purpose: "We need your image to identify you", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ LimitDisclosure: &required, @@ -828,7 +828,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirementsLimitDisclosur vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:777", @@ -846,7 +846,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirementsLimitDisclosur }, { ID: "http://example.edu/credentials/888", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:888", @@ -1007,7 +1007,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements() { Group: []string{"A"}, Purpose: "Your age should be greater or equal to 18.", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1023,7 +1023,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements() { Group: []string{"drivers_license_image"}, Purpose: "We need your photo to identify you", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1039,7 +1039,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements() { Group: []string{"passport_image"}, Purpose: "We need your image to identify you", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1061,7 +1061,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:777", @@ -1239,7 +1239,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements2() { Group: []string{"drivers_license_image"}, Purpose: "We need your photo to identify you", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1256,7 +1256,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements2() { Group: []string{"passport_image"}, Purpose: "We need your photo to identify you", Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1272,7 +1272,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements2() { ID: "flight_training_1", Group: []string{"flight_training"}, Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1292,7 +1292,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements2() { ID: "employment_private_1", Group: []string{"pilot_employment"}, Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1312,7 +1312,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements2() { ID: "employment_gov", Group: []string{"pilot_employment"}, Schema: []*Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, Constraints: &Constraints{ Fields: []*Field{{ @@ -1340,7 +1340,7 @@ func ExamplePresentationDefinition_CreateVP_submissionRequirements2() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.dmv/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:777", @@ -1835,7 +1835,7 @@ const exampleJSONLDContext = `{ func fetchVC(ctx, types []string) *verifiable.Credential { vc, err := createCredential(credentialProto{ - Context: append([]string{verifiable.ContextURI}, ctx...), + Context: append([]string{verifiable.V1ContextURI}, ctx...), Types: append([]string{verifiable.VCType}, types...), ID: "http://test.credential.com/123", Issuer: &verifiable.Issuer{ID: "http://test.issuer.com"}, diff --git a/presexch/example_v2_test.go b/presexch/example_v2_test.go index 4f33c11..088385f 100644 --- a/presexch/example_v2_test.go +++ b/presexch/example_v2_test.go @@ -75,7 +75,7 @@ func ExamplePresentationDefinition_CreateVP_v2() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", @@ -199,7 +199,7 @@ func ExamplePresentationDefinition_CreateVP_with_LdpVC_Format() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", @@ -326,7 +326,7 @@ func ExamplePresentationDefinition_CreateVP_with_Ldp_Format() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", @@ -453,7 +453,7 @@ func ExamplePresentationDefinition_CreateVP_withFormatInInputDescriptor() { vp, err := pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", @@ -580,7 +580,7 @@ func TestExamplePresentationDefinition_CreateVPWithFormat_NoMatch(t *testing.T) _, err = pd.CreateVP(createExampleCredentials([]credentialProto{ { ID: "http://example.edu/credentials/777", - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", @@ -844,7 +844,7 @@ func ExamplePresentationDefinition_CreateVP_limitedDisclosureSkipsNonSDVCs() { makeVC := func(id string) *verifiable.Credential { vc, makeErr := verifiable.CreateCredential(verifiable.CredentialContents{ ID: id, - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: "did:example:76e12ec712ebc6f1c221ebfeb1f", diff --git a/presexch/match_submission_requirements_test.go b/presexch/match_submission_requirements_test.go index 546052a..4e8afd9 100644 --- a/presexch/match_submission_requirements_test.go +++ b/presexch/match_submission_requirements_test.go @@ -166,7 +166,7 @@ func TestInstance_GetSubmissionRequirements(t *testing.T) { ID: uuid.New().String(), InputDescriptors: []*presexch.InputDescriptor{{ Schema: []*presexch.Schema{{ - URI: fmt.Sprintf("%s#%s", verifiable.ContextID, verifiable.VCType), + URI: fmt.Sprintf("%s#%s", verifiable.V1ContextID, verifiable.VCType), }}, ID: uuid.New().String(), Constraints: &presexch.Constraints{ @@ -182,7 +182,7 @@ func TestInstance_GetSubmissionRequirements(t *testing.T) { vc := createTestCredential(t, credentialProto{ ID: "https://issuer.oidp.uscis.gov/credentials/83627465", Context: []string{ - verifiable.ContextURI, + verifiable.V1ContextURI, "https://www.w3.org/2018/credentials/examples/v1", "https://w3id.org/security/bbs/v1", }, @@ -307,7 +307,7 @@ func TestInstance_GetSubmissionRequirements(t *testing.T) { ID: uuid.New().String(), Group: []string{"A"}, Schema: []*presexch.Schema{{ - URI: verifiable.ContextURI, + URI: verifiable.V1ContextURI, }}, Constraints: &presexch.Constraints{ SubjectIsIssuer: &subIsIssuerRequired, @@ -320,13 +320,14 @@ func TestInstance_GetSubmissionRequirements(t *testing.T) { result, err := pd.MatchSubmissionRequirement([]*verifiable.Credential{ createTestCredential(t, credentialProto{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.New().String(), CustomFields: map[string]interface{}{ "first_name": "Jesse", }, }), createTestCredential(t, credentialProto{ + Context: []string{verifiable.V1ContextURI}, ID: uuid.New().String(), Subject: []verifiable.Subject{{ID: issuerID}}, Issuer: &verifiable.Issuer{ID: issuerID}, diff --git a/presexch/package_test.go b/presexch/package_test.go index 830e739..f54d253 100644 --- a/presexch/package_test.go +++ b/presexch/package_test.go @@ -74,7 +74,7 @@ func Test_mDLNestedCtx(t *testing.T) { cred, err := verifiable.CreateCredential(verifiable.CredentialContents{ Context: []string{ - verifiable.ContextURI, + verifiable.V1ContextURI, "https://trustbloc.github.io/context/vc/examples/mdl-v1.jsonld", }, Types: []string{verifiable.VCType, "mDL"}, diff --git a/status/resolver/resolver_test.go b/status/resolver/resolver_test.go index a028e77..7b35205 100644 --- a/status/resolver/resolver_test.go +++ b/status/resolver/resolver_test.go @@ -34,7 +34,7 @@ const ( func TestResolve(t *testing.T) { //nolint:maintidx srcVC, err := verifiable.CreateCredential(verifiable.CredentialContents{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, ID: uuid.NewString(), Schemas: []verifiable.TypedID{}, diff --git a/status/status_test.go b/status/status_test.go index d3cefb5..206cb1c 100644 --- a/status/status_test.go +++ b/status/status_test.go @@ -201,6 +201,7 @@ func TestClient_VerifyStatus(t *testing.T) { }, Resolver: &mockResolver{ Cred: createTestCredential(t, verifiable.CredentialContents{ + Context: []string{verifiable.V1ContextURI}, Issuer: &verifiable.Issuer{ ID: issuerID, }, @@ -284,7 +285,7 @@ func mockStatusVC(t *testing.T, issuerID string, vcStatus isRevoked) *verifiable require.NoError(t, err) return createTestCredential(t, verifiable.CredentialContents{ - Context: []string{verifiable.ContextURI}, + Context: []string{verifiable.V1ContextURI}, Types: []string{verifiable.VCType}, Issuer: &verifiable.Issuer{ ID: issuerID, diff --git a/verifiable/credential.go b/verifiable/credential.go index 3677fba..8a4abed 100644 --- a/verifiable/credential.go +++ b/verifiable/credential.go @@ -13,7 +13,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "log" "net/http" "os" @@ -50,8 +50,8 @@ const ( jsonLDStructureErrStr = "JSON-LD doc has different structure after compaction" ) -// DefaultSchemaTemplate describes default schema. -const DefaultSchemaTemplate = `{ +// SchemaTemplateV1 describes credentials v1 schema. +const SchemaTemplateV1 = `{ "required": [ "@context" %s @@ -242,13 +242,349 @@ const DefaultSchemaTemplate = `{ } ` -// https://www.w3.org/TR/vc-data-model/#data-schemas -const jsonSchema2018Type = "JsonSchemaValidator2018" +// SchemaTemplateV2 describes credential V2 schema. +const SchemaTemplateV2 = `{ + "$id": "https://www.w3.org/2022/credentials/v2/verifiable-credential-schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "JSON Schema for a Verifiable Credential according to the Verifiable Credentials Data Model v2", + "type": "object", + "$defs": { + "type": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "minItems": 1 + } + ] + }, + "credentialSubject": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "minProperties": 1 + }, + "credentialSchema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type" + } + }, + "required": [ + "id", + "type" + ], + "additionalProperties": true + }, + "credentialStatus": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type" + } + }, + "required": [ + "id", + "type" + ], + "additionalProperties": true + }, + "refreshService": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type" + } + }, + "required": [ + "id", + "type" + ], + "additionalProperties": true + }, + "termsOfUse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type" + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + "evidence": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type" + } + }, + "required": [ + "type" + ], + "additionalProperties": true + }, + "proof": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/type" + }, + "proofPurpose": { + "type": "string" + }, + "verificationMethod": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "controller": { + "type": "string" + } + }, + "required": ["id", "type", "controller"], + "additionalProperties": true + } + } + ] + }, + "created": { + "type": "string" + }, + "domain": { + "type": "string" + }, + "challenge": { + "type": "string" + }, + "proofValue": { + "type": "string" + } + }, + "required": [ + "type", + "proofPurpose", + "verificationMethod", + "created" + ], + "additionalProperties": true + }, + "proofChain": { + "type": "array", + "items": { + "$ref": "#/$defs/proof" + }, + "minItems": 1 + } + }, + "properties": { + "@context": { + "type": "array", + "contains": { + "const": "https://www.w3.org/ns/credentials/v2" + }, + "minItems": 1 + }, + "id": { + "type": "string" + }, + "type": { + "oneOf": [ + { + "type": "array", + "contains": { + "const": "VerifiableCredential" + } + }, + { + "type": "string", + "enum": ["VerifiableCredential"] + } + ] + }, + "issuer": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "additionalProperties": true + } + ] + }, + "validFrom": { + "type": "string", + "pattern": "-?([1-9][0-9]{3,}|0[0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?|(24:00:00(\\.0+)?))(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))" + }, + "validUntil": { + "type": "string", + "pattern": "-?([1-9][0-9]{3,}|0[0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?|(24:00:00(\\.0+)?))(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))" + }, + "credentialSubject": { + "oneOf": [ + { + "$ref": "#/$defs/credentialSubject" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/credentialSubject" + }, + "minItems": 1 + } + ] + }, + "credentialStatus": { + "oneOf": [ + { + "$ref": "#/$defs/credentialStatus" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/credentialStatus" + }, + "minItems": 1 + } + ] + }, + "credentialSchema": { + "oneOf": [ + { + "$ref": "#/$defs/credentialSchema" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/credentialSchema" + }, + "minItems": 1 + } + ] + }, + "refreshService": { + "oneOf": [ + { + "$ref": "#/$defs/refreshService" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/refreshService" + }, + "minItems": 1 + } + ] + }, + "termsOfUse": { + "oneOf": [ + { + "$ref": "#/$defs/termsOfUse" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/termsOfUse" + }, + "minItems": 1 + } + ] + }, + "evidence": { + "oneOf": [ + { + "$ref": "#/$defs/evidence" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/evidence" + }, + "minItems": 1 + } + ] + }, + "proof": { + "oneOf": [ + { + "$ref": "#/$defs/proof" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/proof" + }, + "minItems": 1 + } + ] + }, + "proofChain": { + "$ref": "#/$defs/proofChain" + } + }, + "required": [ + "@context" + %s + ], + "additionalProperties": true +} +` const ( - // https://www.w3.org/TR/vc-data-model/#base-context - baseContext = "https://www.w3.org/2018/credentials/v1" + // https://www.w3.org/TR/vc-data-model/#data-schemas + jsonSchema2018Type = "JsonSchemaValidator2018" + + // https://www.w3.org/TR/vc-json-schema/#jsonschema + jsonSchemaType = "JsonSchema" + // https://www.w3.org/TR/vc-json-schema/#jsonschemacredential + jsonSchemaCredentialType = "JsonSchemaCredential" +) +const ( // https://www.w3.org/TR/vc-data-model/#types vcType = "VerifiableCredential" @@ -360,7 +696,7 @@ func (b *CredentialSchemaLoaderBuilder) Build() *CredentialSchemaLoader { } if l.jsonLoader == nil { - l.jsonLoader = defaultSchemaLoader() + l.jsonLoader = schemaLoaderV1() } return l @@ -653,6 +989,8 @@ const ( jsonFldTermsOfUse = "termsOfUse" jsonFldRefreshService = "refreshService" jsonFldSDJWTHashAlg = "_sd_alg" + jsonFldValidFrom = "validFrom" + jsonFldValidUntil = "validUntil" ) // CombinedProofChecker universal proof checker for both LD and JWT proofs. @@ -679,19 +1017,19 @@ type CredentialTemplate func() *Credential // credentialOpts holds options for the Verifiable Credential decoding. type credentialOpts struct { - ldProofChecker lddocument.ProofChecker - jwtProofChecker jwt.ProofChecker - cwtProofChecker cwt.ProofChecker - disabledCustomSchema bool - schemaLoader *CredentialSchemaLoader - modelValidationMode vcModelValidationMode - allowedCustomContexts map[string]bool - allowedCustomTypes map[string]bool - disabledProofCheck bool - strictValidation bool - defaultSchema string - disableValidation bool - verifyDataIntegrity *verifyDataIntegrityOpts + ldProofChecker lddocument.ProofChecker + jwtProofChecker jwt.ProofChecker + cwtProofChecker cwt.ProofChecker + disabledCustomSchema bool + schemaLoader *CredentialSchemaLoader + modelValidationMode vcModelValidationMode + allowedContexts map[string]bool + allowedCustomTypes map[string]bool + disabledProofCheck bool + strictValidation bool + defaultSchema string + disableValidation bool + verifyDataIntegrity *verifyDataIntegrityOpts jsonldCredentialOpts } @@ -776,9 +1114,10 @@ func WithJSONLDValidation() CredentialOpt { // WithBaseContextValidation validates that only the fields and values (when applicable) are present // in the document. No extra fields are allowed (outside of credentialSubject). -func WithBaseContextValidation() CredentialOpt { +func WithBaseContextValidation(baseContext string) CredentialOpt { return func(opts *credentialOpts) { opts.modelValidationMode = baseContextValidation + opts.allowedContexts = map[string]bool{baseContext: true} } } @@ -804,16 +1143,16 @@ func WithExpectedDataIntegrityFields(purpose, domain, challenge string) Credenti // WithBaseContextExtendedValidation validates that fields that are specified in base context are as specified. // Additional fields are allowed. -func WithBaseContextExtendedValidation(customContexts, customTypes []string) CredentialOpt { +func WithBaseContextExtendedValidation(baseContext string, customContexts, customTypes []string) CredentialOpt { return func(opts *credentialOpts) { opts.modelValidationMode = baseContextExtendedValidation - opts.allowedCustomContexts = make(map[string]bool) + opts.allowedContexts = make(map[string]bool) for _, context := range customContexts { - opts.allowedCustomContexts[context] = true + opts.allowedContexts[context] = true } - opts.allowedCustomContexts[baseContext] = true + opts.allowedContexts[baseContext] = true opts.allowedCustomTypes = make(map[string]bool) for _, context := range customTypes { @@ -1108,15 +1447,15 @@ func validateCredential(vcc *CredentialContents, vcJSON JSONObject, vcOpts *cred // Validate VC using JSON schema. Even in case of VC data model extension (i.e. more than one @context // is defined and thus JSON-LD validation is made), it's reasonable to do JSON Schema validation // prior to the JSON-LD one as the former does not check several aspects like mandatory fields or fields format. - err := validateJSONSchema(vcJSON, vcc, vcOpts) + err := validateCredentialUsingJSONSchema(vcJSON, vcc, vcOpts) if err != nil { return err } - return validateJSONLD(vcJSON, vcOpts) + return validateJSONLD(vcJSON, vcc, vcOpts) case jsonldValidation: - return validateJSONLD(vcJSON, vcOpts) + return validateJSONLD(vcJSON, vcc, vcOpts) case baseContextValidation: return validateBaseContext(vcJSON, vcc, vcOpts) @@ -1134,17 +1473,17 @@ func validateBaseContext(vcJSON JSONObject, vcc *CredentialContents, vcOpts *cre return errors.New("violated type constraint: not base only type defined") } - if len(vcc.Context) > 1 || vcc.Context[0] != baseContext { + if len(vcc.Context) > 1 || !vcOpts.allowedContexts[vcc.Context[0]] { return errors.New("violated @context constraint: not base only @context defined") } - return validateJSONSchema(vcJSON, vcc, vcOpts) + return validateCredentialUsingJSONSchema(vcJSON, vcc, vcOpts) } func validateBaseContextWithExtendedValidation(vcJSON JSONObject, vcc *CredentialContents, vcOpts *credentialOpts) error { for _, vcContext := range vcc.Context { - if _, ok := vcOpts.allowedCustomContexts[vcContext]; !ok { + if _, ok := vcOpts.allowedContexts[vcContext]; !ok { return fmt.Errorf("not allowed @context: %s", vcContext) } } @@ -1155,10 +1494,15 @@ func validateBaseContextWithExtendedValidation(vcJSON JSONObject, vcc *Credentia } } - return validateJSONSchema(vcJSON, vcc, vcOpts) + return validateCredentialUsingJSONSchema(vcJSON, vcc, vcOpts) } -func validateJSONLD(vcJSON JSONObject, vcOpts *credentialOpts) error { +func validateJSONLD(vcJSON JSONObject, vcc *CredentialContents, vcOpts *credentialOpts) error { + baseContext, err := GetBaseContext(vcc.Context) + if err != nil { + return err + } + // TODO: docjsonld.ValidateJSONLDMap has bug that it modify contexts of input vcJSON. Fix in did-go validateOpts := []docjsonld.ValidateOpts{ docjsonld.WithDocumentLoader(vcOpts.jsonldCredentialOpts.jsonldDocumentLoader), @@ -1239,11 +1583,25 @@ func parseCredentialContents(raw JSONObject, isSDJWT bool) (*CredentialContents, return nil, fmt.Errorf("fill credential issued from raw: %w", err) } + if issued == nil { + issued, err = parseTimeFld(raw, jsonFldValidFrom) + if err != nil { + return nil, fmt.Errorf("fill credential issued from raw: %w", err) + } + } + expired, err := parseTimeFld(raw, jsonFldExpired) if err != nil { return nil, fmt.Errorf("fill credential expired from raw: %w", err) } + if expired == nil { + expired, err = parseTimeFld(raw, jsonFldValidUntil) + if err != nil { + return nil, fmt.Errorf("fill credential expired from raw: %w", err) + } + } + status, err := newNilableTypedID(raw[jsonFldStatus]) if err != nil { return nil, fmt.Errorf("fill credential status from raw: %w", err) @@ -1559,7 +1917,7 @@ func getCredentialOpts(opts []CredentialOpt) *credentialOpts { func newDefaultSchemaLoader() *CredentialSchemaLoader { return &CredentialSchemaLoader{ schemaDownloadClient: &http.Client{}, - jsonLoader: defaultSchemaLoader(), + jsonLoader: schemaLoaderV1(), } } @@ -1577,16 +1935,12 @@ func SerializeSubject(subject []Subject) interface{} { return mapSlice(subject, SubjectToJSON) } -func validateJSONSchema(vcJSON JSONObject, vcc *CredentialContents, opts *credentialOpts) error { - return validateCredentialUsingJSONSchema(vcJSON, vcc.Schemas, opts) -} - -func validateCredentialUsingJSONSchema(vcJSON JSONObject, schemas []TypedID, opts *credentialOpts) error { - // Validate that the Verifiable Credential conforms to the serialization of the Verifiable Credential data model - // (https://w3c.github.io/vc-data-model/#example-1-a-simple-example-of-a-verifiable-credential) - schemaLoader, err := getSchemaLoader(schemas, opts) +// validateCredentialUsingJSONSchema validates that the Verifiable Credential conforms to the serialization of the Verifiable Credential data model +// (https://w3c.github.io/vc-data-model/#example-1-a-simple-example-of-a-verifiable-credential) +func validateCredentialUsingJSONSchema(vcJSON JSONObject, vcc *CredentialContents, opts *credentialOpts) error { + schemaLoader, err := getSchemaLoader(vcc, opts) if err != nil { - return err + return fmt.Errorf("get schema loader: %w", err) } loader := gojsonschema.NewGoLoader(vcJSON) @@ -1604,28 +1958,43 @@ func validateCredentialUsingJSONSchema(vcJSON JSONObject, schemas []TypedID, opt return nil } -func getSchemaLoader(schemas []TypedID, opts *credentialOpts) (gojsonschema.JSONLoader, error) { +func getSchemaLoader(vcc *CredentialContents, opts *credentialOpts) (gojsonschema.JSONLoader, error) { + var schemaLoader gojsonschema.JSONLoader + + if IsBaseContext(vcc.Context, V2ContextURI) { + schemaLoader = schemaLoaderV2() + } else { + schemaLoader = schemaLoaderV1() + } + if opts.disabledCustomSchema { - return defaultSchemaLoaderWithOpts(opts), nil + return schemaLoader, nil + } + + if opts.defaultSchema != "" { + return gojsonschema.NewStringLoader(opts.defaultSchema), nil } - for _, schema := range schemas { + for _, schema := range vcc.Schemas { switch schema.Type { - case jsonSchema2018Type: + case jsonSchema2018Type, jsonSchemaType: customSchemaData, err := getJSONSchema(schema.ID, opts) if err != nil { return nil, fmt.Errorf("load of custom credential schema from %s: %w", schema.ID, err) } return gojsonschema.NewBytesLoader(customSchemaData), nil + //TODO: add support for JSON Schema Credential + case jsonSchemaCredentialType: + // TODO: should unsupported schema type be ignored or should this cause an error? + errLogger.Printf("unsupported credential schema: %s. Using default schema for validation", schema.Type) default: // TODO: should unsupported schema be ignored or should this cause an error? errLogger.Printf("unsupported credential schema: %s. Using default schema for validation", schema.Type) } } - // If no custom schema is chosen, use default one - return defaultSchemaLoaderWithOpts(opts), nil + return schemaLoader, nil } type schemaOpts struct { @@ -1642,15 +2011,26 @@ func WithDisableRequiredField(fieldName string) SchemaOpt { } } -// JSONSchemaLoader creates default schema with the option to disable the check of specific properties. -func JSONSchemaLoader(opts ...SchemaOpt) string { - defaultRequired := []string{ +// JSONSchemaLoaderV1 creates default schema with the option to disable the check of specific properties. +func JSONSchemaLoaderV1(opts ...SchemaOpt) string { + return jsonSchemaLoader(SchemaTemplateV1, []string{ schemaPropertyType, schemaPropertyCredentialSubject, schemaPropertyIssuer, schemaPropertyIssuanceDate, - } + }, opts...) +} +// JSONSchemaLoaderV2 creates default schema with the option to disable the check of specific properties. +func JSONSchemaLoaderV2(opts ...SchemaOpt) string { + return jsonSchemaLoader(SchemaTemplateV2, []string{ + schemaPropertyType, + schemaPropertyCredentialSubject, + schemaPropertyIssuer, + }, opts...) +} + +func jsonSchemaLoader(schemaTemplate string, defaultRequired []string, opts ...SchemaOpt) string { dsOpts := &schemaOpts{} for _, opt := range opts { opt(dsOpts) @@ -1673,19 +2053,15 @@ func JSONSchemaLoader(opts ...SchemaOpt) string { } } - return fmt.Sprintf(DefaultSchemaTemplate, required) + return fmt.Sprintf(schemaTemplate, required) } -func defaultSchemaLoaderWithOpts(opts *credentialOpts) gojsonschema.JSONLoader { - if opts.defaultSchema != "" { - return gojsonschema.NewStringLoader(opts.defaultSchema) - } - - return defaultSchemaLoader() +func schemaLoaderV1() gojsonschema.JSONLoader { + return gojsonschema.NewStringLoader(JSONSchemaLoaderV1()) } -func defaultSchemaLoader() gojsonschema.JSONLoader { - return gojsonschema.NewStringLoader(JSONSchemaLoader()) +func schemaLoaderV2() gojsonschema.JSONLoader { + return gojsonschema.NewStringLoader(JSONSchemaLoaderV2()) } func getJSONSchema(url string, opts *credentialOpts) ([]byte, error) { @@ -1731,7 +2107,7 @@ func loadJSONSchema(url string, client *http.Client) ([]byte, error) { var gotBody []byte - gotBody, err = ioutil.ReadAll(resp.Body) + gotBody, err = io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("credential schema: read response body: %w", err) } @@ -1891,12 +2267,20 @@ func serializeCredentialContents(vcc *CredentialContents, proofs []Proof) (JSONO vcJSON[jsonFldTermsOfUse] = typedIDsToRaw(vcc.TermsOfUse) } - if vcc.Issued != nil { - vcJSON[jsonFldIssued] = serializeTime(vcc.Issued) + fillTimes := func(issuedField, expiredField string) { + if vcc.Issued != nil { + vcJSON[issuedField] = serializeTime(vcc.Issued) + } + + if vcc.Expired != nil { + vcJSON[expiredField] = serializeTime(vcc.Expired) + } } - if vcc.Expired != nil { - vcJSON[jsonFldExpired] = serializeTime(vcc.Expired) + if IsBaseContext(vcc.Context, V2ContextURI) { + fillTimes(jsonFldValidFrom, jsonFldValidUntil) + } else { + fillTimes(jsonFldIssued, jsonFldExpired) } if vcc.SDJWTHashAlg != nil { @@ -2063,6 +2447,34 @@ func (vc *Credential) WithModifiedExpired(wrapper *util.TimeWrapper) *Credential } } +// WithModifiedValidFrom creates new credential with modified issued time and without proofs as they become invalid. +func (vc *Credential) WithModifiedValidFrom(wrapper *util.TimeWrapper) *Credential { + newCredJSON := copyCredentialJSONWithoutProofs(vc.credentialJSON) + newContents := vc.Contents() + + newContents.Issued = wrapper + newCredJSON[jsonFldValidFrom] = serializeTime(wrapper) + + return &Credential{ + credentialJSON: newCredJSON, + credentialContents: newContents, + } +} + +// WithModifiedValidUntil creates new credential with modified expired time and without proofs as they become invalid. +func (vc *Credential) WithModifiedValidUntil(wrapper *util.TimeWrapper) *Credential { + newCredJSON := copyCredentialJSONWithoutProofs(vc.credentialJSON) + newContents := vc.Contents() + + newContents.Expired = wrapper + newCredJSON[jsonFldValidUntil] = serializeTime(wrapper) + + return &Credential{ + credentialJSON: newCredJSON, + credentialContents: newContents, + } +} + // WithModifiedContext creates new credential with modified context and without proofs as they become invalid. func (vc *Credential) WithModifiedContext(context []string) *Credential { newCredJSON := copyCredentialJSONWithoutProofs(vc.credentialJSON) diff --git a/verifiable/credential_jws_test.go b/verifiable/credential_jws_test.go index 836872c..02fc861 100644 --- a/verifiable/credential_jws_test.go +++ b/verifiable/credential_jws_test.go @@ -22,10 +22,30 @@ import ( "github.com/trustbloc/kms-go/spi/kms" ) -func TestJWTCredClaimsMarshalJWS(t *testing.T) { +func TestV1JWTCredClaimsMarshalJWS(t *testing.T) { proofCreator, proofChecker := testsupport.NewKMSSigVerPair(t, kms.RSARS256Type, "did:example:76e12ec712ebc6f1c221ebfeb1f#key1") - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) + require.NoError(t, err) + + jwtClaims, err := vc.JWTClaims(true) + require.NoError(t, err) + + t.Run("Marshal signed JWT", func(t *testing.T) { + jws, err := jwtClaims.MarshalJWSString(RS256, proofCreator, "did:example:76e12ec712ebc6f1c221ebfeb1f#key1") + require.NoError(t, err) + + jwtVC, err := ParseCredential([]byte(jws), WithProofChecker(proofChecker)) + + require.NoError(t, err) + require.Equal(t, vc.stringJSON(t), jsonObjectToString(t, jwtVC.ToRawJSON())) + }) +} + +func TestV2JWTCredClaimsMarshalJWS(t *testing.T) { + proofCreator, proofChecker := testsupport.NewKMSSigVerPair(t, kms.RSARS256Type, "did:example:76e12ec712ebc6f1c221ebfeb1f#key1") + + vc, err := parseTestCredential(t, []byte(v2ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) jwtClaims, err := vc.JWTClaims(true) @@ -48,7 +68,7 @@ type invalidCredClaims struct { Credential int `json:"vc,omitempty"` } -func TestCredJWSDecoderUnmarshal(t *testing.T) { +func TestV1CredJWSDecoderUnmarshal(t *testing.T) { verificationKeyID := "did:example:76e12ec712ebc6f1c221ebfeb1f#key1" otherVerificationKeyID := "did:example:76e12ec712ebc6f1c221ebfeb1f#key2" diff --git a/verifiable/credential_jwt.go b/verifiable/credential_jwt.go index ab7cef3..b6f3785 100644 --- a/verifiable/credential_jwt.go +++ b/verifiable/credential_jwt.go @@ -26,6 +26,8 @@ const ( vcExpirationDateField = "expirationDate" vcIssuerField = "issuer" vcIssuerIDField = "id" + vcValidFrom = "validFrom" + vcValidUntil = "validUntil" ) // JWTCredClaims is JWT Claims extension by Verifiable Credential (with custom "vc" claim). @@ -126,6 +128,8 @@ func newJWTCredClaims(vc *Credential, minimizeVC bool) (*JWTCredClaims, error) { if minimizeVC { delete(credentialJSONCopy, jsonFldExpired) delete(credentialJSONCopy, jsonFldIssued) + delete(credentialJSONCopy, jsonFldValidUntil) + delete(credentialJSONCopy, jsonFldValidFrom) delete(credentialJSONCopy, jsonFldID) issuer, err := parseIssuer(credentialJSONCopy[jsonFldIssuer]) @@ -188,7 +192,11 @@ func (jcc *JWTCredClaims) refineFromJWTClaims() error { if nbf := claims.NotBefore; nbf != nil { nbfTime := nbf.Time().UTC() - vcMap[vcIssuanceDateField] = nbfTime.Format(time.RFC3339) + if HasBaseContext(vcMap, V2ContextURI) { + vcMap[vcValidFrom] = nbfTime.Format(time.RFC3339) + } else { + vcMap[vcIssuanceDateField] = nbfTime.Format(time.RFC3339) + } } if jti := claims.ID; jti != "" { @@ -197,12 +205,20 @@ func (jcc *JWTCredClaims) refineFromJWTClaims() error { if iat := claims.IssuedAt; iat != nil { iatTime := iat.Time().UTC() - vcMap[vcIssuanceDateField] = iatTime.Format(time.RFC3339) + if HasBaseContext(vcMap, V2ContextURI) { + vcMap[vcValidFrom] = iatTime.Format(time.RFC3339) + } else { + vcMap[vcIssuanceDateField] = iatTime.Format(time.RFC3339) + } } if exp := claims.Expiry; exp != nil { expTime := exp.Time().UTC() - vcMap[vcExpirationDateField] = expTime.Format(time.RFC3339) + if HasBaseContext(vcMap, V2ContextURI) { + vcMap[vcValidUntil] = expTime.Format(time.RFC3339) + } else { + vcMap[vcExpirationDateField] = expTime.Format(time.RFC3339) + } } return nil diff --git a/verifiable/credential_jwt_test.go b/verifiable/credential_jwt_test.go index 3eb5a95..6daa204 100644 --- a/verifiable/credential_jwt_test.go +++ b/verifiable/credential_jwt_test.go @@ -33,9 +33,6 @@ func TestRefineVcFromJwtClaims(t *testing.T) { vcID := "http://example.edu/credentials/3732" expired := time.Date(2029, time.August, 10, 0, 0, 0, 0, time.UTC) - vcMap := map[string]interface{}{ - "issuer": "unknown", - } credClaims := &jwt.Claims{ Issuer: issuerID, NotBefore: josejwt.NewNumericDate(issued), @@ -44,16 +41,36 @@ func TestRefineVcFromJwtClaims(t *testing.T) { Expiry: josejwt.NewNumericDate(expired), } - jwtCredClaims := &JWTCredClaims{ - Claims: credClaims, - VC: vcMap, - } + t.Run("Credentials V1", func(t *testing.T) { + vcMap := map[string]interface{}{ + "issuer": "unknown", + } + jwtCredClaims := &JWTCredClaims{ + Claims: credClaims, + VC: vcMap, + } + + require.NoError(t, jwtCredClaims.refineFromJWTClaims()) + require.Equal(t, issuerID, vcMap["issuer"]) + require.Equal(t, "2019-08-10T00:00:00Z", vcMap["issuanceDate"]) + require.Equal(t, "2029-08-10T00:00:00Z", vcMap["expirationDate"]) + }) - jwtCredClaims.refineFromJWTClaims() + t.Run("Credentials V2", func(t *testing.T) { + vcMap := map[string]interface{}{ + "@context": []string{V2ContextURI}, + "issuer": "unknown", + } + jwtCredClaims := &JWTCredClaims{ + Claims: credClaims, + VC: vcMap, + } - require.Equal(t, issuerID, vcMap["issuer"]) - require.Equal(t, "2019-08-10T00:00:00Z", vcMap["issuanceDate"]) - require.Equal(t, "2029-08-10T00:00:00Z", vcMap["expirationDate"]) + require.NoError(t, jwtCredClaims.refineFromJWTClaims()) + require.Equal(t, issuerID, vcMap["issuer"]) + require.Equal(t, "2019-08-10T00:00:00Z", vcMap["validFrom"]) + require.Equal(t, "2029-08-10T00:00:00Z", vcMap["validUntil"]) + }) } func TestJWTCredClaims_ToSDJWTCredentialPayload(t *testing.T) { diff --git a/verifiable/credential_jwt_unsecured_test.go b/verifiable/credential_jwt_unsecured_test.go index 440ba05..e01d007 100644 --- a/verifiable/credential_jwt_unsecured_test.go +++ b/verifiable/credential_jwt_unsecured_test.go @@ -14,7 +14,7 @@ import ( ) func TestCredentialJWTClaimsMarshallingToUnsecuredJWT(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) jwtClaims, err := vc.JWTClaims(true) @@ -37,7 +37,7 @@ func TestCredentialJWTClaimsMarshallingToUnsecuredJWT(t *testing.T) { func TestCredUnsecuredJWTDecoderParseJWTClaims(t *testing.T) { t.Run("Successful unsecured JWT decoding", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) jwtClaims, err := vc.JWTClaims(true) diff --git a/verifiable/credential_ldp_test.go b/verifiable/credential_ldp_test.go index 254879e..23152a9 100644 --- a/verifiable/credential_ldp_test.go +++ b/verifiable/credential_ldp_test.go @@ -46,20 +46,39 @@ func TestParseCredentialFromLinkedDataProof_Ed25519Signature2018(t *testing.T) { VerificationMethod: "did:example:76e12ec712ebc6f1c221ebfeb1f#key1", } - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + t.Run("V1", func(t *testing.T) { + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) - r.NoError(err) + r.NoError(err) - err = vc.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t))) - r.NoError(err) + err = vc.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t))) + r.NoError(err) - vcBytes, err := json.Marshal(vc) - r.NoError(err) + vcBytes, err := json.Marshal(vc) + r.NoError(err) - vcWithLdp, err := parseTestCredential(t, vcBytes, - WithProofChecker(proofChecker)) - r.NoError(err) - r.Equal(vc.ToRawJSON(), vcWithLdp.ToRawJSON()) + vcWithLdp, err := parseTestCredential(t, vcBytes, + WithProofChecker(proofChecker)) + r.NoError(err) + r.Equal(vc.ToRawJSON(), vcWithLdp.ToRawJSON()) + }) + + t.Run("V2", func(t *testing.T) { + vc, err := parseTestCredential(t, []byte(v2ValidCredential), WithDisabledProofCheck()) + + r.NoError(err) + + err = vc.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t))) + r.NoError(err) + + vcBytes, err := json.Marshal(vc) + r.NoError(err) + + vcWithLdp, err := parseTestCredential(t, vcBytes, + WithProofChecker(proofChecker)) + r.NoError(err) + r.Equal(vc.ToRawJSON(), vcWithLdp.ToRawJSON()) + }) } func TestParseCredentialFromLinkedDataProof_Ed25519Signature2020(t *testing.T) { @@ -75,23 +94,41 @@ func TestParseCredentialFromLinkedDataProof_Ed25519Signature2020(t *testing.T) { VerificationMethod: "did:example:76e12ec712ebc6f1c221ebfeb1f#key1", } - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) - r.NoError(err) + t.Run("V1", func(t *testing.T) { + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) + r.NoError(err) - err = vc.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t))) - r.NoError(err) + err = vc.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t))) + r.NoError(err) - vcBytes, err := json.Marshal(vc) - r.NoError(err) + vcBytes, err := json.Marshal(vc) + r.NoError(err) - vcWithLdp, err := parseTestCredential(t, vcBytes, - WithProofChecker(proofChecker)) - r.NoError(err) - r.Equal(vc.ToRawJSON(), vcWithLdp.ToRawJSON()) + vcWithLdp, err := parseTestCredential(t, vcBytes, + WithProofChecker(proofChecker)) + r.NoError(err) + r.Equal(vc.ToRawJSON(), vcWithLdp.ToRawJSON()) + }) + + t.Run("V2", func(t *testing.T) { + vc, err := parseTestCredential(t, []byte(v2ValidCredential), WithDisabledProofCheck()) + r.NoError(err) + + err = vc.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t))) + r.NoError(err) + + vcBytes, err := json.Marshal(vc) + r.NoError(err) + + vcWithLdp, err := parseTestCredential(t, vcBytes, + WithProofChecker(proofChecker)) + r.NoError(err) + r.Equal(vc.ToRawJSON(), vcWithLdp.ToRawJSON()) + }) } //nolint:lll -func TestParseCredentialFromLinkedDataProof_JSONLD_Validation(t *testing.T) { +func TestParseV1CredentialFromLinkedDataProof_JSONLD_Validation(t *testing.T) { r := require.New(t) pubKeyBytes := base58.Decode("DqS5F3GVe3rCxucgi4JBNagjv4dKoHc8TDLDw9kR58Pz") @@ -394,7 +431,7 @@ func TestExtraContextWithLDP(t *testing.T) { vcMap, err := jsonutil.ToMap(vcBytes) r.NoError(err) - vcMap["@context"] = baseContext + vcMap["@context"] = V1ContextURI vcBytes, err = json.Marshal(vcMap) r.NoError(err) @@ -600,7 +637,7 @@ func TestParseCredentialFromLinkedDataProof_JsonWebSignature2020_Ed25519(t *test VerificationMethod: "did:example:76e12ec712ebc6f1c221ebfeb1f#key1", } - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) r.NoError(err) err = vc.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t))) @@ -627,7 +664,7 @@ func TestParseCredentialFromLinkedDataProof_JsonWebSignature2020_ecdsaP256(t *te VerificationMethod: "did:example:76e12ec712ebc6f1c221ebfeb1f#key1", } - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) r.NoError(err) err = vc.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t))) @@ -655,7 +692,7 @@ func TestParseCredentialFromLinkedDataProof_EcdsaSecp256k1Signature2019(t *testi VerificationMethod: "did:example:76e12ec712ebc6f1c221ebfeb1f#key1", } - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) r.NoError(err) err = vc.AddLinkedDataProof(ldpContext, jsonldsig.WithDocumentLoader(createTestDocumentLoader(t))) @@ -983,7 +1020,7 @@ func TestParseCredentialWithSeveralLinkedDataProofs(t *testing.T) { {Type: kms.ECDSAP256TypeIEEEP1363, PublicKeyID: "did:example:76e12ec712ebc6f1c221ebfeb1f#key2"}, }) - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) r.NoError(err) err = vc.AddLinkedDataProof(&LinkedDataProofContext{ @@ -1020,7 +1057,7 @@ func TestCredential_AddLinkedDataProof(t *testing.T) { "did:example:76e12ec712ebc6f1c221ebfeb1f#key-1") t.Run("Add a valid JWS Linked Data proof to VC", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) r.NoError(err) originalVCMap, err := jsonutil.ToMap(vc) @@ -1060,7 +1097,7 @@ func TestCredential_AddLinkedDataProof(t *testing.T) { }) t.Run("Add invalid Linked Data proof to VC", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) ldpContextWithMissingSignatureType := &LinkedDataProofContext{ @@ -1074,7 +1111,7 @@ func TestCredential_AddLinkedDataProof(t *testing.T) { t.Run("sign and verify proof with capabilityChain", func(t *testing.T) { rootCapability := "https://edv.com/foo/zcap/123" - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) r.NoError(err) err = vc.AddLinkedDataProof(&LinkedDataProofContext{ diff --git a/verifiable/credential_test.go b/verifiable/credential_test.go index 4e8585b..7cf4801 100644 --- a/verifiable/credential_test.go +++ b/verifiable/credential_test.go @@ -91,7 +91,7 @@ var vccProto = CredentialContents{ func TestParseCredential(t *testing.T) { t.Run("test creation of new Verifiable Credential from JSON with valid structure", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithStrictValidation(), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithStrictValidation(), WithDisabledProofCheck()) require.NoError(t, err) require.NotNil(t, vc) @@ -163,9 +163,9 @@ func TestParseCredential(t *testing.T) { func TestParseCredentialWithoutIssuanceDate(t *testing.T) { t.Run("test creation of new Verifiable Credential with disabled issuance date check", func(t *testing.T) { - schema := JSONSchemaLoader(WithDisableRequiredField("issuanceDate")) + schema := JSONSchemaLoaderV1(WithDisableRequiredField("issuanceDate")) - vc, err := parseTestCredential(t, []byte(credentialWithoutIssuanceDate), WithDisabledProofCheck(), + vc, err := parseTestCredential(t, []byte(v1CredentialWithoutIssuanceDate), WithDisabledProofCheck(), WithStrictValidation(), WithSchema(schema)) require.NoError(t, err) @@ -173,30 +173,52 @@ func TestParseCredentialWithoutIssuanceDate(t *testing.T) { }) t.Run("'issuanceDate is required' error", func(t *testing.T) { - _, err := parseTestCredential(t, []byte(credentialWithoutIssuanceDate), WithDisabledProofCheck(), + _, err := parseTestCredential(t, []byte(v1CredentialWithoutIssuanceDate), WithDisabledProofCheck(), WithStrictValidation()) require.Error(t, err) }) } -func TestValidateVerCredContext(t *testing.T) { +func TestParseV2CredentialWithoutValidFrom(t *testing.T) { + t.Run("test creation of new Verifiable Credential with disabled issuer check", func(t *testing.T) { + schema := JSONSchemaLoaderV2(WithDisableRequiredField("issuer")) + + vc, err := parseTestCredential(t, []byte(v2CredentialWithoutIssuer), WithDisabledProofCheck(), + WithStrictValidation(), + WithSchema(schema)) + require.NoError(t, err) + require.NotNil(t, vc) + }) + + t.Run("'issuer is required' error", func(t *testing.T) { + _, err := parseTestCredential(t, []byte(v2CredentialWithoutIssuer), WithDisabledProofCheck(), + WithStrictValidation()) + require.ErrorContains(t, err, "issuer is required") + }) +} + +func TestValidateV1VerCredContext(t *testing.T) { t.Run("test verifiable credential with a single context", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) - raw[jsonFldContext] = []string{"https://www.w3.org/2018/credentials/v1"} + cc := &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}} + + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) + raw[jsonFldContext] = cc.Context - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, cc, &credentialOpts{}) require.NoError(t, err) }) t.Run("test verifiable credential with a single invalid context", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) - raw[jsonFldContext] = []string{"https://www.w3.org/2018/credentials/v2"} + cc := &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v2"}} + + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) + raw[jsonFldContext] = cc.Context - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, cc, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "does not match: \"https://www.w3.org/2018/credentials/v1\"") }) @@ -204,10 +226,10 @@ func TestValidateVerCredContext(t *testing.T) { t.Run("test verifiable credential with empty context", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) delete(raw, jsonFldContext) - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "@context is required") }) @@ -215,26 +237,30 @@ func TestValidateVerCredContext(t *testing.T) { t.Run("test verifiable credential with multiple contexts", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) - raw[jsonFldContext] = []string{ + cc := &CredentialContents{Context: []string{ "https://www.w3.org/2018/credentials/v1", "https://www.w3.org/2018/credentials/examples/v1", - } + }} + + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) + raw[jsonFldContext] = cc.Context - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, cc, &credentialOpts{}) require.NoError(t, err) }) t.Run("test verifiable credential with multiple invalid contexts", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) - raw[jsonFldContext] = []string{ + cc := &CredentialContents{Context: []string{ "https://www.w3.org/2018/credentials/v2", "https://www.w3.org/2018/credentials/examples/v1", - } + }} + + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) + raw[jsonFldContext] = cc.Context - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, cc, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "@context.0: @context.0 does not match: \"https://www.w3.org/2018/credentials/v1\"") }) @@ -242,7 +268,7 @@ func TestValidateVerCredContext(t *testing.T) { t.Run("test verifiable credential with object context", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldContext] = []interface{}{"https://www.w3.org/2018/credentials/examples/v1", map[string]interface{}{ "image": map[string]string{ @@ -250,12 +276,41 @@ func TestValidateVerCredContext(t *testing.T) { }, }} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "@context.0: @context.0 does not match: \"https://www.w3.org/2018/credentials/v1\"") }) } +func TestValidateV2VerCredContext(t *testing.T) { + t.Run("test verifiable credential with a single context", func(t *testing.T) { + var raw JSONObject + + cc := &CredentialContents{Context: []string{"https://www.w3.org/ns/credentials/v2"}} + + require.NoError(t, json.Unmarshal([]byte(v2ValidCredential), &raw)) + raw[jsonFldContext] = cc.Context + + err := validateCredentialUsingJSONSchema(raw, cc, &credentialOpts{}) + require.NoError(t, err) + }) + + t.Run("test verifiable credential with multiple contexts", func(t *testing.T) { + var raw JSONObject + + cc := &CredentialContents{Context: []string{ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/examples/v2", + }} + + require.NoError(t, json.Unmarshal([]byte(v2ValidCredential), &raw)) + raw[jsonFldContext] = cc.Context + + err := validateCredentialUsingJSONSchema(raw, cc, &credentialOpts{}) + require.NoError(t, err) + }) +} + // func TestValidateVerCredID(t *testing.T) { // raw := JSONObject{} // @@ -273,10 +328,10 @@ func TestValidateVerCredType(t *testing.T) { t.Run("test verifiable credential with no type", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldType] = []string{} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "Array must have at least 1 items") }) @@ -284,10 +339,12 @@ func TestValidateVerCredType(t *testing.T) { t.Run("test verifiable credential with not first VerifiableCredential type", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + cc := &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}} + + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldType] = []string{"NotVerifiableCredential"} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, cc, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "Does not match pattern '^VerifiableCredential$") }) @@ -295,10 +352,10 @@ func TestValidateVerCredType(t *testing.T) { t.Run("test verifiable credential with VerifiableCredential type only as string", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldType] = "VerifiableCredential" - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) @@ -306,10 +363,10 @@ func TestValidateVerCredType(t *testing.T) { func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldType] = []string{"UniversityDegreeCredentail", "VerifiableCredential"} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) } @@ -318,10 +375,10 @@ func TestValidateVerCredCredentialSubject(t *testing.T) { t.Run("test verifiable credential with no credential subject", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) delete(raw, jsonFldSubject) - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "credentialSubject is required") }) @@ -330,21 +387,21 @@ func TestValidateVerCredCredentialSubject(t *testing.T) { var raw JSONObject var subject interface{} - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) require.NoError(t, json.Unmarshal([]byte(singleCredentialSubject), &subject)) raw[jsonFldSubject] = subject - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) t.Run("test verifiable credential with single string credential subject", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldSubject] = "did:example:ebfeb1f712ebc6f1c276e12ec21" - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) @@ -352,7 +409,7 @@ func TestValidateVerCredCredentialSubject(t *testing.T) { var raw JSONObject var subject interface{} - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) require.NoError(t, json.Unmarshal([]byte(multipleCredentialSubjects), &subject)) raw[jsonFldSubject] = subject @@ -363,11 +420,11 @@ func TestValidateVerCredCredentialSubject(t *testing.T) { t.Run("test verifiable credential with invalid type of credential subject", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldSubject] = 55 - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "credentialSubject: Invalid type.") }) @@ -377,10 +434,10 @@ func TestValidateVerCredIssuer(t *testing.T) { t.Run("test verifiable credential with no issuer", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) delete(raw, jsonFldIssuer) - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "issuer is required") }) @@ -388,11 +445,11 @@ func TestValidateVerCredIssuer(t *testing.T) { t.Run("test verifiable credential with plain id issuer", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldIssuer] = "https://example.edu/issuers/14" - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) @@ -400,22 +457,22 @@ func TestValidateVerCredIssuer(t *testing.T) { var raw JSONObject var issuer interface{} - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) require.NoError(t, json.Unmarshal([]byte(issuerAsObject), &issuer)) raw[jsonFldIssuer] = issuer - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) t.Run("test verifiable credential with invalid type of issuer", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldIssuer] = 55 - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "issuer: Invalid type") }) @@ -423,14 +480,14 @@ func TestValidateVerCredIssuer(t *testing.T) { t.Run("test verifiable credential with string issuer", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) issuerRaw, err := json.Marshal("not-a-uri-issuer") require.NoError(t, err) raw[jsonFldIssuer] = issuerRaw - err = validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err = validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "issuer: Does not match format 'uri'") }) @@ -438,7 +495,7 @@ func TestValidateVerCredIssuer(t *testing.T) { t.Run("test verifiable credential with object issuer", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) issuerRaw := map[string]interface{}{ "id": "not-a-uri-issuer-id", @@ -447,7 +504,7 @@ func TestValidateVerCredIssuer(t *testing.T) { raw[jsonFldIssuer] = issuerRaw - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "issuer.id: Does not match format 'uri'") }) @@ -457,10 +514,10 @@ func TestValidateVerCredIssuanceDate(t *testing.T) { t.Run("test verifiable credential with empty issuance date", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) delete(raw, jsonFldIssued) - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "issuanceDate is required") }) @@ -468,10 +525,10 @@ func TestValidateVerCredIssuanceDate(t *testing.T) { t.Run("test verifiable credential with wrong format of issuance date", func(t *testing.T) { var vcMap map[string]interface{} - require.NoError(t, json.Unmarshal([]byte(validCredential), &vcMap)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &vcMap)) vcMap["issuanceDate"] = "not a valid date time" - err := validateCredentialUsingJSONSchema(vcMap, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(vcMap, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "issuanceDate: Does not match format 'date-time'") }) @@ -479,10 +536,10 @@ func TestValidateVerCredIssuanceDate(t *testing.T) { for _, timeStr := range []string{"2010-01-01T19:23:24Z", "2010-01-01T19:23:24.385Z"} { var vcMap map[string]interface{} - require.NoError(t, json.Unmarshal([]byte(validCredential), &vcMap)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &vcMap)) vcMap["issuanceDate"] = timeStr - err := validateCredentialUsingJSONSchema(vcMap, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(vcMap, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) } } @@ -491,7 +548,7 @@ func TestValidateVerCredProof(t *testing.T) { t.Run("test verifiable credential with embedded proof", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) proof := []Proof{{ "type": "Ed25519Signature2018", @@ -503,16 +560,16 @@ func TestValidateVerCredProof(t *testing.T) { raw[jsonFldLDProof] = proof - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) t.Run("test verifiable credential with empty proof", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldLDProof] = nil - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) } @@ -521,20 +578,20 @@ func TestValidateVerCredExpirationDate(t *testing.T) { t.Run("test verifiable credential with empty expiration date", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldExpired] = nil - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) t.Run("test verifiable credential with wrong format of expiration date", func(t *testing.T) { var vcMap map[string]interface{} - require.NoError(t, json.Unmarshal([]byte(validCredential), &vcMap)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &vcMap)) vcMap["expirationDate"] = "not a valid date time" - err := validateCredentialUsingJSONSchema(vcMap, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(vcMap, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "expirationDate: Does not match format 'date-time'") }) @@ -542,10 +599,10 @@ func TestValidateVerCredExpirationDate(t *testing.T) { for _, timeStr := range []string{"2010-01-01T19:23:24Z", "2010-01-01T19:23:24.385Z"} { var vcMap map[string]interface{} - require.NoError(t, json.Unmarshal([]byte(validCredential), &vcMap)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &vcMap)) vcMap["expirationDate"] = timeStr - err := validateCredentialUsingJSONSchema(vcMap, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(vcMap, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) } } @@ -554,20 +611,20 @@ func TestValidateVerCredStatus(t *testing.T) { t.Run("test verifiable credential with empty credential status", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldStatus] = nil - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) t.Run("test verifiable credential with undefined id of credential status", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldStatus] = &TypedID{Type: "CredentialStatusList2017"} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "credentialStatus: id is required") }) @@ -575,10 +632,10 @@ func TestValidateVerCredStatus(t *testing.T) { t.Run("test verifiable credential with undefined type of credential status", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldStatus] = &TypedID{ID: "https://example.edu/status/24"} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "credentialStatus: type is required") }) @@ -586,10 +643,10 @@ func TestValidateVerCredStatus(t *testing.T) { t.Run("test verifiable credential with invalid URL of id of credential status", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldStatus] = map[string]interface{}{"id": "invalid URL", "type": "CredentialStatusList2017"} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "credentialStatus.id: Does not match format 'uri'") }) @@ -599,19 +656,19 @@ func TestValidateVerCredSchema(t *testing.T) { t.Run("test verifiable credential with empty credential schema", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) t.Run("test verifiable credential with undefined id of credential schema", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldSchema] = &TypedID{Type: "JsonSchemaValidator2018"} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "credentialSchema: id is required") }) @@ -619,10 +676,10 @@ func TestValidateVerCredSchema(t *testing.T) { t.Run("test verifiable credential with undefined type of credential schema", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldSchema] = &TypedID{ID: "https://example.org/examples/degree.json"} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "credentialSchema: type is required") }) @@ -630,10 +687,10 @@ func TestValidateVerCredSchema(t *testing.T) { t.Run("test verifiable credential with invalid URL of id of credential schema", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldSchema] = map[string]interface{}{"id": "invalid URL", "type": "JsonSchemaValidator2018"} - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "credentialSchema.id: Does not match format 'uri'") }) @@ -643,48 +700,48 @@ func TestValidateVerCredRefreshService(t *testing.T) { t.Run("test verifiable credential with empty refresh service", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) raw[jsonFldRefreshService] = nil - err := validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err := validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.NoError(t, err) }) t.Run("test verifiable credential with undefined id of refresh service", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) raw := vc.ToRawJSON() raw[jsonFldRefreshService] = map[string]interface{}{"type": "ManualRefreshService2018"} - err = validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err = validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "refreshService: id is required") }) t.Run("test verifiable credential with undefined type of refresh service", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) raw := vc.ToRawJSON() raw[jsonFldRefreshService] = map[string]interface{}{"id": "https://example.edu/refresh/3732"} - err = validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err = validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "refreshService: type is required") }) t.Run("test verifiable credential with invalid URL of id of credential schema", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) raw := vc.ToRawJSON() raw[jsonFldRefreshService] = map[string]interface{}{"id": "invalid URL", "type": "ManualRefreshService2018"} - err = validateCredentialUsingJSONSchema(raw, nil, &credentialOpts{}) + err = validateCredentialUsingJSONSchema(raw, &CredentialContents{Context: []string{"https://www.w3.org/2018/credentials/v1"}}, &credentialOpts{}) require.Error(t, err) require.Contains(t, err.Error(), "refreshService.id: Does not match format 'uri'") }) @@ -693,7 +750,7 @@ func TestValidateVerCredRefreshService(t *testing.T) { func TestCredential_MarshalJSON(t *testing.T) { t.Run("round trip conversion of credential with plain issuer", func(t *testing.T) { // setup -> create verifiable credential from json byte data - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) require.NotEmpty(t, vc) @@ -764,7 +821,7 @@ func TestCredential_MarshalJSON(t *testing.T) { t.Run("round trip conversion of credential with composite issuer", func(t *testing.T) { // setup -> create verifiable credential from json byte data - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) require.NotEmpty(t, vc) @@ -789,7 +846,7 @@ func TestCredential_MarshalJSON(t *testing.T) { }) t.Run("Failure in cf marshalling", func(t *testing.T) { - vc, err := CreateCredential(CredentialContents{}, map[string]interface{}{ + vc, err := CreateCredential(CredentialContents{Context: []string{V1ContextURI}}, map[string]interface{}{ "invalid field": make(chan int), }) require.NoError(t, err) @@ -801,6 +858,7 @@ func TestCredential_MarshalJSON(t *testing.T) { t.Run("Failure in TermsOfUse marshalling", func(t *testing.T) { vc, err := CreateCredential(CredentialContents{ + Context: []string{V1ContextURI}, TermsOfUse: []TypedID{{CustomFields: map[string]interface{}{ "invalidField": make(chan int), }}}, @@ -1074,7 +1132,7 @@ func TestWithCredDisableValidation(t *testing.T) { func TestWithCredentialSchemaLoader(t *testing.T) { httpClient := &http.Client{} - jsonSchemaLoader := gojsonschema.NewStringLoader(JSONSchemaLoader()) + jsonSchemaLoader := gojsonschema.NewStringLoader(JSONSchemaLoaderV1()) cache := NewExpirableSchemaCache(100, 10*time.Minute) credentialOpt := WithCredentialSchemaLoader( @@ -1113,23 +1171,24 @@ func TestWithJSONLDValidation(t *testing.T) { opts := &credentialOpts{} credentialOpt(opts) require.Equal(t, jsonldValidation, opts.modelValidationMode) - require.Empty(t, opts.allowedCustomContexts) + require.Empty(t, opts.allowedContexts) require.Empty(t, opts.allowedCustomTypes) } func TestWithBaseContextValidation(t *testing.T) { - credentialOpt := WithBaseContextValidation() + credentialOpt := WithBaseContextValidation(V1ContextURI) require.NotNil(t, credentialOpt) opts := &credentialOpts{} credentialOpt(opts) require.Equal(t, baseContextValidation, opts.modelValidationMode) - require.Empty(t, opts.allowedCustomContexts) + require.True(t, opts.allowedContexts[V1ContextURI]) require.Empty(t, opts.allowedCustomTypes) } func TestWithBaseContextExtendedValidation(t *testing.T) { credentialOpt := WithBaseContextExtendedValidation( + V1ContextURI, []string{"https://www.w3.org/2018/credentials/examples/v1"}, []string{"UniversityDegreeCredential", "AlumniCredential"}) require.NotNil(t, credentialOpt) @@ -1142,7 +1201,7 @@ func TestWithBaseContextExtendedValidation(t *testing.T) { "https://www.w3.org/2018/credentials/v1": true, "https://www.w3.org/2018/credentials/examples/v1": true, }, - opts.allowedCustomContexts) + opts.allowedContexts) require.Equal(t, map[string]bool{ "VerifiableCredential": true, @@ -1174,7 +1233,7 @@ func TestWithStrictValidation(t *testing.T) { func TestCustomCredentialJsonSchemaValidator2018(t *testing.T) { testServer := httptest.NewServer(http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) { rawMap := make(map[string]interface{}) - require.NoError(t, json.Unmarshal([]byte(JSONSchemaLoader()), &rawMap)) + require.NoError(t, json.Unmarshal([]byte(JSONSchemaLoaderV1()), &rawMap)) // extend default schema to require new referenceNumber field to be mandatory required, success := rawMap["required"].([]interface{}) @@ -1193,7 +1252,7 @@ func TestCustomCredentialJsonSchemaValidator2018(t *testing.T) { defer func() { testServer.Close() }() var raw JSONObject - err := json.Unmarshal([]byte(validCredential), &raw) + err := json.Unmarshal([]byte(v1ValidCredential), &raw) require.NoError(t, err) // define credential schema @@ -1221,16 +1280,19 @@ func TestCustomCredentialJsonSchemaValidator2018(t *testing.T) { require.NoError(t, err) vc, err := parseTestCredential(t, customValidSchema, WithDisabledProofCheck(), - WithBaseContextExtendedValidation([]string{ - "https://www.w3.org/2018/credentials/v1", - "https://www.w3.org/2018/credentials/examples/v1", - "https://w3id.org/security/jws/v1", - "https://trustbloc.github.io/context/vc/examples-v1.jsonld", - "https://w3id.org/security/suites/ed25519-2020/v1", - }, []string{ - "VerifiableCredential", - "UniversityDegreeCredential", - })) + WithBaseContextExtendedValidation( + V1ContextURI, + []string{ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1", + "https://w3id.org/security/jws/v1", + "https://trustbloc.github.io/context/vc/examples-v1.jsonld", + "https://w3id.org/security/suites/ed25519-2020/v1", + }, []string{ + "VerifiableCredential", + "UniversityDegreeCredential", + }, + )) require.NoError(t, err) vcc := vc.Contents() @@ -1244,7 +1306,7 @@ func TestCustomCredentialJsonSchemaValidator2018(t *testing.T) { t.Run("Error when failed to download custom credentialSchema", func(t *testing.T) { var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) // define credential schema with invalid port raw[jsonFldSchema] = map[string]interface{}{"id": "http://localhost:0001", "type": "JsonSchemaValidator2018"} @@ -1262,7 +1324,7 @@ func TestCustomCredentialJsonSchemaValidator2018(t *testing.T) { t.Run("Uses default schema if custom credentialSchema is not of 'JsonSchemaValidator2018' type", func(t *testing.T) { //nolint:lll var raw JSONObject - require.NoError(t, json.Unmarshal([]byte(validCredential), &raw)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &raw)) // define credential schema with not supported type raw[jsonFldSchema] = map[string]interface{}{"id": testServer.URL, "type": "ZkpExampleSchema2018"} @@ -1297,7 +1359,7 @@ func TestDownloadCustomSchema(t *testing.T) { noCacheOpts := &credentialOpts{schemaLoader: newDefaultSchemaLoader()} withCacheOpts := &credentialOpts{schemaLoader: &CredentialSchemaLoader{ schemaDownloadClient: httpClient, - jsonLoader: gojsonschema.NewStringLoader(JSONSchemaLoader()), + jsonLoader: gojsonschema.NewStringLoader(JSONSchemaLoaderV1()), cache: NewExpirableSchemaCache(32*1024*1024, time.Hour), }} @@ -1339,7 +1401,7 @@ func TestDownloadCustomSchema(t *testing.T) { // Check for cache expiration. withCacheOpts = &credentialOpts{schemaLoader: &CredentialSchemaLoader{ schemaDownloadClient: httpClient, - jsonLoader: gojsonschema.NewStringLoader(JSONSchemaLoader()), + jsonLoader: gojsonschema.NewStringLoader(JSONSchemaLoaderV1()), cache: NewExpirableSchemaCache(32*1024*1024, time.Second), }} loadsCount = 0 @@ -1462,7 +1524,7 @@ func Test_SubjectID(t *testing.T) { } func TestRawCredentialSerialization(t *testing.T) { - cBytes := []byte(validCredential) + cBytes := []byte(v1ValidCredential) rc := new(JSONObject) err := json.Unmarshal(cBytes, rc) @@ -1741,7 +1803,7 @@ func Test_JWTVCToJSON(t *testing.T) { issuerKeyID := "did:example:76e12ec712ebc6f1c221ebfeb1f" proofCreator, _ := testsupport.NewKMSSigVerPair(t, kms.ED25519Type, issuerKeyID) - vcSource, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vcSource, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) jwtClaims, err := vcSource.JWTClaims(true) @@ -1831,7 +1893,7 @@ func TestParseCredentialFromRaw(t *testing.T) { } func TestParseCredentialFromRaw_PreserveDates(t *testing.T) { - vcMap, err := jsonutil.ToMap(validCredential) + vcMap, err := jsonutil.ToMap(v1ValidCredential) require.NoError(t, err) vcMap["issuanceDate"] = "2020-01-01T00:00:00.000Z" @@ -1863,7 +1925,7 @@ func TestCredential_validateCredential(t *testing.T) { r := require.New(t) t.Run("test jsonldValidation constraint", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) r.NoError(err) vcOpts := []CredentialOpt{ @@ -1897,7 +1959,7 @@ func TestCredential_validateCredential(t *testing.T) { }) t.Run("test baseContextValidation constraint", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) raw := vc.ToRawJSON() @@ -1905,36 +1967,36 @@ func TestCredential_validateCredential(t *testing.T) { raw[jsonFldType] = []string{"VerifiableCredential"} raw[jsonFldContext] = []string{"https://www.w3.org/2018/credentials/v1"} - _, err = ParseCredentialJSON(raw, WithBaseContextValidation()) + _, err = ParseCredentialJSON(raw, WithBaseContextValidation(V1ContextURI)) r.NoError(err) raw[jsonFldType] = []string{"VerifiableCredential", "UniversityDegreeCredential"} raw[jsonFldContext] = []string{"https://www.w3.org/2018/credentials/v1"} - _, err = ParseCredentialJSON(raw, WithBaseContextValidation()) + _, err = ParseCredentialJSON(raw, WithBaseContextValidation(V1ContextURI)) r.Error(err) r.EqualError(err, "violated type constraint: not base only type defined") raw[jsonFldType] = []string{"UniversityDegreeCredential"} raw[jsonFldContext] = []string{"https://www.w3.org/2018/credentials/v1"} - _, err = ParseCredentialJSON(raw, WithBaseContextValidation()) + _, err = ParseCredentialJSON(raw, WithBaseContextValidation(V1ContextURI)) r.Error(err) r.EqualError(err, "violated type constraint: not base only type defined") raw[jsonFldType] = []string{"VerifiableCredential"} raw[jsonFldContext] = []string{"https://www.w3.org/2018/credentials/v1", "https://www.exaple.org/udc/v1"} - _, err = ParseCredentialJSON(raw, WithBaseContextValidation()) + _, err = ParseCredentialJSON(raw, WithBaseContextValidation(V1ContextURI)) r.Error(err) r.EqualError(err, "violated @context constraint: not base only @context defined") raw[jsonFldType] = []string{"VerifiableCredential"} raw[jsonFldContext] = []string{"https://www.exaple.org/udc/v1"} - _, err = ParseCredentialJSON(raw, WithBaseContextValidation()) + _, err = ParseCredentialJSON(raw, WithBaseContextValidation(V1ContextURI)) r.Error(err) r.EqualError(err, "violated @context constraint: not base only @context defined") }) t.Run("test baseContextExtendedValidation constraint", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) raw := vc.ToRawJSON() @@ -1945,6 +2007,7 @@ func TestCredential_validateCredential(t *testing.T) { _, err = ParseCredentialJSON( raw, WithBaseContextExtendedValidation( + V1ContextURI, []string{"https://www.w3.org/2018/credentials/v1", "https://www.exaple.org/alumni/v1"}, []string{"VerifiableCredential", "AlumniCredential"}, )) @@ -1956,6 +2019,7 @@ func TestCredential_validateCredential(t *testing.T) { _, err = ParseCredentialJSON( raw, WithBaseContextExtendedValidation( + V1ContextURI, []string{"https://www.w3.org/2018/credentials/v1", "https://www.exaple.org/alumni/v1"}, []string{"VerifiableCredential", "AlumniCredential"}, )) @@ -1967,6 +2031,7 @@ func TestCredential_validateCredential(t *testing.T) { _, err = ParseCredentialJSON( raw, WithBaseContextExtendedValidation( + V1ContextURI, []string{"https://www.w3.org/2018/credentials/v1", "https://www.exaple.org/alumni/v1"}, []string{"VerifiableCredential", "AlumniCredential"}, )) @@ -2021,7 +2086,7 @@ func TestParseCredentialWithDisabledProofCheck(t *testing.T) { t.Run("ParseUnverifiedCredential() for JWS", func(t *testing.T) { // Prepare JWS. - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) credClaims, err := vc.JWTClaims(true) @@ -2043,7 +2108,7 @@ func TestParseCredentialWithDisabledProofCheck(t *testing.T) { }) t.Run("ParseUnverifiedCredential() for JWT error cases", func(t *testing.T) { - validCred, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + validCred, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) vcc := validCred.Contents() @@ -2066,13 +2131,13 @@ func TestParseCredentialWithDisabledProofCheck(t *testing.T) { WithDisabledProofCheck(), WithJSONLDValidation()) // Apply only JSON-LD validation require.Error(t, err) - require.ErrorContains(t, err, "invalid context URI on position") + require.ErrorContains(t, err, "unsupported @context: https://w3id.org/security/bbs/v1") require.Nil(t, vcUnverified) }) t.Run("ParseUnverifiedCredential() for Linked Data proof", func(t *testing.T) { // Prepare JWS. - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) created := time.Now() @@ -2117,7 +2182,7 @@ func TestParseCredentialWithDisabledProofCheck(t *testing.T) { var rawVCMap map[string]interface{} - require.NoError(t, json.Unmarshal([]byte(validCredential), &rawVCMap)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &rawVCMap)) rawVCMap["@context"] = 55 // should be string or slice of strings rawVCMapBytes, err := json.Marshal(rawVCMap) @@ -2128,7 +2193,7 @@ func TestParseCredentialWithDisabledProofCheck(t *testing.T) { require.Contains(t, err.Error(), "context of unknown type") require.Nil(t, vc) - require.NoError(t, json.Unmarshal([]byte(validCredential), &rawVCMap)) + require.NoError(t, json.Unmarshal([]byte(v1ValidCredential), &rawVCMap)) delete(rawVCMap, "issuer") rawVCMapBytes, err = json.Marshal(rawVCMap) @@ -2144,14 +2209,14 @@ func TestParseCredentialWithDisabledProofCheck(t *testing.T) { func TestCredential_ValidateCredential(t *testing.T) { t.Run("Success", func(t *testing.T) { - vc, err := ParseCredential([]byte(validCredential), WithCredDisableValidation(), WithDisabledProofCheck()) + vc, err := ParseCredential([]byte(v1ValidCredential), WithCredDisableValidation(), WithDisabledProofCheck()) require.NoError(t, err) require.NotNil(t, vc) require.NoError(t, vc.ValidateCredential(WithJSONLDDocumentLoader(createTestDocumentLoader(t)))) }) t.Run("Invalid cred", func(t *testing.T) { - vc, err := ParseCredential([]byte(credentialWithoutIssuanceDate), + vc, err := ParseCredential([]byte(v1CredentialWithoutIssuanceDate), WithCredDisableValidation(), WithDisabledProofCheck()) require.NoError(t, err) require.NotNil(t, vc) @@ -2161,7 +2226,7 @@ func TestCredential_ValidateCredential(t *testing.T) { func TestMarshalCredential(t *testing.T) { t.Run("test marshalling VC to JSON bytes", func(t *testing.T) { - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) require.NoError(t, err) require.NotNil(t, vc) diff --git a/verifiable/jsonld.go b/verifiable/jsonld.go index 786cd2e..dc9b43d 100644 --- a/verifiable/jsonld.go +++ b/verifiable/jsonld.go @@ -5,13 +5,84 @@ SPDX-License-Identifier: Apache-2.0 package verifiable +import ( + "errors" + "fmt" +) + const ( - // ContextURI is the required JSON-LD context for VCs and VPs. - ContextURI = "https://www.w3.org/2018/credentials/v1" - // ContextID is the non-fragment part of the JSON-LD schema ID for VCs and VPs. - ContextID = "https://www.w3.org/2018/credentials" + // V1ContextURI is the required JSON-LD context for VCs and VPs. + V1ContextURI = "https://www.w3.org/2018/credentials/v1" + // V1ContextID is the non-fragment part of the JSON-LD schema ID for VCs and VPs. + V1ContextID = "https://www.w3.org/2018/credentials" + + V2ContextURI = "https://www.w3.org/ns/credentials/v2" + V2ContextID = "https://www.w3.org/ns/credentials" + // VCType is the required Type for Verifiable Credentials. VCType = "VerifiableCredential" // VPType is the required Type for Verifiable Credentials. VPType = "VerifiablePresentation" ) + +// GetBaseContext gets the base context from the contexts. +// The base context is the first element in the array and must be one of: +// - https://www.w3.org/2018/credentials/v1 +// - https://www.w3.org/ns/credentials/v2 +func GetBaseContext(contexts []string) (string, error) { + if len(contexts) == 0 { + return "", errors.New("@context is required") + } + + ctx := contexts[0] + + if ctx == V1ContextURI || ctx == V2ContextURI { + return ctx, nil + } + + return "", fmt.Errorf("unsupported @context: %s", ctx) +} + +// GetBaseContextFromRawDocument gets the base context from the raw document. +// The @context is either a string or array of strings. If it's given as an array +// then the base context is the first element in the @context array and must be one of: +// +// - https://www.w3.org/2018/credentials/v1 +// - https://www.w3.org/ns/credentials/v2 +func GetBaseContextFromRawDocument(doc map[string]interface{}) (string, error) { + ctx, ok := doc[jsonFldContext] + if !ok { + return "", fmt.Errorf("%s is required", jsonFldContext) + } + + baseContext, _, err := decodeContext(ctx) + if err != nil { + return "", err + } + + return GetBaseContext(baseContext) +} + +// IsBaseContext returns true if the given context is the base context. +func IsBaseContext(contexts []string, ctx string) bool { + if len(contexts) == 0 { + return false + } + + return contexts[0] == ctx +} + +// HasBaseContext returns true if the given document has the given base context. +func HasBaseContext(doc map[string]interface{}, ctx string) bool { + rawContext, ok := doc[jsonFldContext] + if !ok { + return false + } + + contexts, _, err := decodeContext(rawContext) + if err != nil { + return false + } + + return IsBaseContext(contexts, ctx) +} diff --git a/verifiable/presentation.go b/verifiable/presentation.go index f7055f0..45758a7 100644 --- a/verifiable/presentation.go +++ b/verifiable/presentation.go @@ -20,7 +20,7 @@ import ( docjsonld "github.com/trustbloc/did-go/doc/ld/validator" ) -const basePresentationSchema = ` +const v1BasePresentationSchema = ` { "required": [ "@context", @@ -155,8 +155,161 @@ const basePresentationSchema = ` } ` +const v2BasePresentationSchema = `{ + "$id": "https://www.w3.org/2022/credentials/v2/verifiable-presentation-schema.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "JSON Schema for a Verifiable Presentation according to the Verifiable Credentials Data Model v2", + "type": "object", + "$defs": { + "proof": { + "type": "object", + "properties": { + "type": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "minItems": 1 + } + ] + }, + "proofPurpose": { + "type": "string" + }, + "verificationMethod": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "controller": { + "type": "string" + } + }, + "required": [ + "id", + "type", + "controller" + ], + "additionalProperties": true + } + } + ] + }, + "created": { + "type": "string", + "pattern": "-?([1-9][0-9]{3,}|0[0-9]{3})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]+)?|(24:00:00(\\.0+)?))(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))" + }, + "domain": { + "type": "string" + }, + "challenge": { + "type": "string" + }, + "proofValue": { + "type": "string" + } + }, + "required": [ + "type", + "proofPurpose", + "verificationMethod", + "created" + ], + "additionalProperties": true + }, + "proofChain": { + "type": "array", + "items": { + "$ref": "#/$defs/proof" + }, + "minItems": 1 + } + }, + "properties": { + "@context": { + "type": "array", + "contains": { + "const": "https://www.w3.org/ns/credentials/v2" + }, + "minItems": 1 + }, + "id": { + "type": "string" + }, + "type": { + "oneOf": [ + { + "type": "array", + "contains": { + "const": "VerifiablePresentation" + } + }, + { + "type": "string", + "enum": ["VerifiablePresentation"] + } + ] + }, + "verifiableCredential": { + "anyOf": [ + { + "type": "array" + }, + { + "type": "object" + }, + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "proof": { + "oneOf": [ + { + "$ref": "#/$defs/proof" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/proof" + }, + "minItems": 1 + } + ] + }, + "proofChain": { + "$ref": "#/$defs/proofChain" + } + }, + "required": [ + "@context", + "type" + ], + "additionalProperties": true +}` + //nolint:gochecknoglobals -var basePresentationSchemaLoader = gojsonschema.NewStringLoader(basePresentationSchema) +var ( + v1BasePresentationSchemaLoader = gojsonschema.NewStringLoader(v1BasePresentationSchema) + v2BasePresentationSchemaLoader = gojsonschema.NewStringLoader(v2BasePresentationSchema) +) // MarshalledCredential defines marshalled Verifiable Credential enclosed into Presentation. // MarshalledCredential can be passed to verifiable.ParseCredential(). @@ -183,7 +336,7 @@ type Presentation struct { // NewPresentation creates a new Presentation with default context and type with the provided credentials. func NewPresentation(opts ...CreatePresentationOpt) (*Presentation, error) { p := Presentation{ - Context: []string{baseContext}, + Context: []string{V1ContextURI}, Type: []string{vpType}, credentials: []*Credential{}, } @@ -636,19 +789,35 @@ func validateVPJSONLD(vpBytes rawPresentation, opts *presentationOpts) error { } func validateVPJSONSchema(data rawPresentation) error { - loader := gojsonschema.NewGoLoader(data) + validate := func(schemaLoader gojsonschema.JSONLoader, data rawPresentation) error { + loader := gojsonschema.NewGoLoader(data) - result, err := gojsonschema.Validate(basePresentationSchemaLoader, loader) - if err != nil { - return fmt.Errorf("validation of verifiable credential: %w", err) + result, err := gojsonschema.Validate(schemaLoader, loader) + if err != nil { + return fmt.Errorf("validation of verifiable credential: %w", err) + } + + if !result.Valid() { + errMsg := describeSchemaValidationError(result, "verifiable presentation") + return errors.New(errMsg) + } + + return nil } - if !result.Valid() { - errMsg := describeSchemaValidationError(result, "verifiable presentation") - return errors.New(errMsg) + baseContext, err := GetBaseContextFromRawDocument(data) + if err != nil { + return err } - return nil + switch baseContext { + case V1ContextURI: + return validate(v1BasePresentationSchemaLoader, data) + case V2ContextURI: + return validate(v2BasePresentationSchemaLoader, data) + default: + return fmt.Errorf("unsupported verifiable presentation context: %s", baseContext) + } } func decodeVPFromJSON(vpData []byte) (rawPresentation, error) { diff --git a/verifiable/presentation_test.go b/verifiable/presentation_test.go index 3e97acd..901934d 100644 --- a/verifiable/presentation_test.go +++ b/verifiable/presentation_test.go @@ -144,7 +144,84 @@ const validPresentationWithCustomFields = ` } ` -//go:embed testdata/validPresentationWithJWTVC.jsonld +const v2ValidPresentation = `{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/examples/v2" + ], + "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", + "type": ["VerifiablePresentation"], + "verifiableCredential": [{ + "@context": "https://www.w3.org/ns/credentials/v2", + "id": "data:application/vc+sd-jwt,QzVjV...RMjU", + "type": "EnvelopedVerifiableCredential", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "validFrom": "2010-01-01T19:23:24Z", + "validUntil": "2026-02-01T19:23:24Z", + "alumniOf": "Example University" + } + }], + "holder": "did:example:ebfeb1f712ebc6f1c276e12ec21" +}` + +const v2PresentationWithoutCredentials = `{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/examples/v2" + ], + "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", + "type": ["VerifiablePresentation"], + "holder": "did:example:ebfeb1f712ebc6f1c276e12ec21" +}` + +const v2ValidPresentationWithCustomFields = ` +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://trustbloc.github.io/context/vc/presentation-exchange-submission-v1.jsonld" + ], + "id": "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", + "type": [ + "VerifiablePresentation", + "PresentationSubmission" + ], + "presentation_submission": { + "descriptor_map": [ + { + "id": "degree_input_1", + "path": "$.verifiableCredential.[0]" + }, + { + "id": "citizenship_input_1", + "path": "$.verifiableCredential.[1]" + } + ] + }, + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/examples/v2" + ], + "id": "http://example.edu/credentials/58473", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu/issuers/14", + "validFrom": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "alumniOf": "Example University" + }, + "proof": { + "type": "RsaSignature2018" + } + } + ], + "holder": "did:example:ebfeb1f712ebc6f1c276e12ec21" +} +` + +//go:embed testdata/v1_validPresentationWithJWTVC.jsonld var validPresentationWithJWTVC []byte //nolint:gochecknoglobals //go:embed testdata/context/presentation_submission_v1.jsonld @@ -440,6 +517,102 @@ func TestParsePresentation(t *testing.T) { }) } +func TestV2ParsePresentation(t *testing.T) { + t.Run("creates a new Verifiable Presentation from JSON with valid structure", func(t *testing.T) { + vp, err := newTestPresentation(t, []byte(v2ValidPresentation), WithPresDisabledProofCheck(), + WithPresStrictValidation()) + require.NoError(t, err) + require.NotNil(t, vp) + + // validate @context + require.Equal(t, []string{ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/examples/v2", + }, vp.Context) + + // check id + require.Equal(t, "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", vp.ID) + + // check type + require.Equal(t, []string{"VerifiablePresentation"}, vp.Type) + + // check verifiableCredentials + require.NotNil(t, vp.Credentials()) + require.Len(t, vp.Credentials(), 1) + + // check holder + require.Equal(t, "did:example:ebfeb1f712ebc6f1c276e12ec21", vp.Holder) + }) + + t.Run("creates a new Verifiable Presentation from valid JSON without credentials", func(t *testing.T) { + vp, err := newTestPresentation(t, []byte(v2PresentationWithoutCredentials), + WithPresDisabledProofCheck(), + WithPresStrictValidation()) + require.NoError(t, err) + require.NotNil(t, vp) + + // validate @context + require.Equal(t, []string{ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/examples/v2", + }, vp.Context) + + // check id + require.Equal(t, "urn:uuid:3978344f-8596-4c3a-a978-8fcaba3903c5", vp.ID) + + // check type + require.Equal(t, []string{"VerifiablePresentation"}, vp.Type) + + // check verifiableCredentials + require.Nil(t, vp.Credentials()) + require.Empty(t, vp.Credentials()) + + // check holder + require.Equal(t, "did:example:ebfeb1f712ebc6f1c276e12ec21", vp.Holder) + + // check rawPresentation + rp, err := vp.raw() + require.NoError(t, err) + + require.IsType(t, nil, rp[vpFldCredential]) + }) + + t.Run("creates a new Verifiable Presentation with custom/additional fields", func(t *testing.T) { + verify := func(t *testing.T, vp *Presentation) { + require.Len(t, vp.CustomFields, 1) + require.Len(t, vp.CustomFields["presentation_submission"], 1) + submission, ok := vp.CustomFields["presentation_submission"].(map[string]interface{}) + require.True(t, ok) + require.Len(t, submission, 1) + descrMap, ok := submission["descriptor_map"].([]interface{}) + require.True(t, ok) + require.Len(t, descrMap, 2) + } + + loader := createTestDocumentLoader(t, ldcontext.Document{ + URL: "https://trustbloc.github.io/context/vc/presentation-exchange-submission-v1.jsonld", + Content: presentationSubmissionV1, + }) + + vp, err := ParsePresentation([]byte(v2ValidPresentationWithCustomFields), + WithPresDisabledProofCheck(), + WithPresJSONLDDocumentLoader(loader)) + require.NoError(t, err) + require.NotNil(t, vp) + verify(t, vp) + + b, e := vp.MarshalJSON() + require.NoError(t, e) + require.NotEmpty(t, b) + + vp, err = ParsePresentation(b, WithPresStrictValidation(), WithPresDisabledProofCheck(), + WithPresJSONLDDocumentLoader(loader)) + require.NoError(t, err) + require.NotNil(t, vp) + verify(t, vp) + }) +} + func TestValidateVP_Context(t *testing.T) { t.Run("rejects verifiable presentation with empty context", func(t *testing.T) { var raw rawPresentation @@ -464,7 +637,7 @@ func TestValidateVP_Context(t *testing.T) { require.NoError(t, err) vp, err := newTestPresentation(t, bytes, WithPresDisabledProofCheck()) require.Error(t, err) - require.Contains(t, err.Error(), "does not match: \"https://www.w3.org/2018/credentials/v1\"") + require.Contains(t, err.Error(), "unsupported @context: https://www.w3.org/2018/credentials/v2") require.Nil(t, vp) }) @@ -487,7 +660,7 @@ func TestValidateVP_Context(t *testing.T) { require.NoError(t, err) vp, err := newTestPresentation(t, bytes, WithPresDisabledProofCheck()) require.Error(t, err) - require.Contains(t, err.Error(), "does not match: \"https://www.w3.org/2018/credentials/v1\"") + require.Contains(t, err.Error(), "unsupported @context: https://www.w3.org/2018/credentials/v2") require.Nil(t, vp) }) } @@ -598,7 +771,7 @@ func TestPresentation_MarshalJSON(t *testing.T) { func TestNewPresentation(t *testing.T) { r := require.New(t) - vc, err := ParseCredential([]byte(validCredential), + vc, err := ParseCredential([]byte(v1ValidCredential), WithJSONLDDocumentLoader(createTestDocumentLoader(t)), WithDisabledProofCheck()) r.NoError(err) @@ -629,7 +802,7 @@ func TestPresentation_decodeCredentials(t *testing.T) { proofCreator, proofChecker := testsupport.NewKMSSigVerPair(t, kms.ED25519Type, "did:example:76e12ec712ebc6f1c221ebfeb1f#k1") - vc, err := parseTestCredential(t, []byte(validCredential), WithDisabledProofCheck()) + vc, err := parseTestCredential(t, []byte(v1ValidCredential), WithDisabledProofCheck()) r.NoError(err) jwtClaims, err := vc.JWTClaims(false) diff --git a/verifiable/support_test.go b/verifiable/support_test.go index 7daa188..ba055e2 100644 --- a/verifiable/support_test.go +++ b/verifiable/support_test.go @@ -22,11 +22,21 @@ import ( "github.com/trustbloc/vc-go/proof/testsupport" ) -//go:embed testdata/valid_credential.jsonld -var validCredential string //nolint:gochecknoglobals +var ( + //go:embed testdata/v1_valid_credential.jsonld + v1ValidCredential string //nolint:gochecknoglobals -//go:embed testdata/credential_without_issuancedate.jsonld -var credentialWithoutIssuanceDate string //nolint:gochecknoglobals + //go:embed testdata/v1_credential_without_issuancedate.jsonld + v1CredentialWithoutIssuanceDate string //nolint:gochecknoglobals +) + +var ( + //go:embed testdata/v2_valid_credential.jsonld + v2ValidCredential string //nolint:gochecknoglobals + + //go:embed testdata/v2_credential_without_issuer.jsonld + v2CredentialWithoutIssuer string //nolint:gochecknoglobals +) func (vc *Credential) stringJSON(t *testing.T) string { bytes, err := json.Marshal(vc) @@ -59,7 +69,7 @@ func (vp *Presentation) stringJSON(t *testing.T) string { func createVCWithLinkedDataProof(t *testing.T) (*Credential, *checker.ProofChecker) { t.Helper() - vc, err := ParseCredential([]byte(validCredential), + vc, err := ParseCredential([]byte(v1ValidCredential), WithJSONLDDocumentLoader(createTestDocumentLoader(t)), WithDisabledProofCheck()) @@ -87,7 +97,7 @@ func createVCWithLinkedDataProof(t *testing.T) (*Credential, *checker.ProofCheck func createVCWithTwoLinkedDataProofs(t *testing.T) (*Credential, *checker.ProofChecker) { t.Helper() - vc, err := ParseCredential([]byte(validCredential), + vc, err := ParseCredential([]byte(v1ValidCredential), WithJSONLDDocumentLoader(createTestDocumentLoader(t)), WithDisabledProofCheck()) diff --git a/verifiable/testdata/credential_without_issuancedate.jsonld b/verifiable/testdata/v1_credential_without_issuancedate.jsonld similarity index 100% rename from verifiable/testdata/credential_without_issuancedate.jsonld rename to verifiable/testdata/v1_credential_without_issuancedate.jsonld diff --git a/verifiable/testdata/validPresentationWithJWTVC.jsonld b/verifiable/testdata/v1_validPresentationWithJWTVC.jsonld similarity index 100% rename from verifiable/testdata/validPresentationWithJWTVC.jsonld rename to verifiable/testdata/v1_validPresentationWithJWTVC.jsonld diff --git a/verifiable/testdata/valid_credential.jsonld b/verifiable/testdata/v1_valid_credential.jsonld similarity index 100% rename from verifiable/testdata/valid_credential.jsonld rename to verifiable/testdata/v1_valid_credential.jsonld diff --git a/verifiable/testdata/v2_credential_without_issuer.jsonld b/verifiable/testdata/v2_credential_without_issuer.jsonld new file mode 100644 index 0000000..e09b067 --- /dev/null +++ b/verifiable/testdata/v2_credential_without_issuer.jsonld @@ -0,0 +1,28 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/examples/v2" + ], + "id": "http://example.edu/credentials/1872", + "type": ["VerifiableCredential", "AlumniCredential"], + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "alumniOf": { + "id": "did:example:c276e12ec21ebfeb1f712ebc6f1", + "name": [{ + "value": "Example University", + "lang": "en" + }, { + "value": "Exemple d'Université", + "lang": "fr" + }] + } + }, + "proof": { + "type": "RsaSignature2018", + "created": "2017-06-18T21:19:10Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "https://example.edu/issuers/565049#key-1", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..TCYt5XsITJX1CxPCT8yAV-TVkIEq_PbChOMqsLfRoPsnsgw5WEuts01mq-pQy7UJiN5mgRxD-WUcX16dUEMGlv50aqzpqh4Qktb3rk-BuQy72IFLOqV0G_zS245-kronKb78cPN25DGlcTwLtjPAYuNzVBAh4vGHSrQyHUdBBPM" + } +} \ No newline at end of file diff --git a/verifiable/testdata/v2_valid_credential.jsonld b/verifiable/testdata/v2_valid_credential.jsonld new file mode 100644 index 0000000..f8d4e0e --- /dev/null +++ b/verifiable/testdata/v2_valid_credential.jsonld @@ -0,0 +1,27 @@ +{ + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/examples/v2" + ], + "id": "http://example.edu/credentials/1872", + "type": ["VerifiableCredential", "AlumniCredential"], + "issuer": { + "id": "did:example:76e12ec712ebc6f1c221ebfeb1f", + "name": "Example University", + "image": "data:image/png;base64,iVBOR" + }, + "validFrom": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "alumniOf": { + "id": "did:example:c276e12ec21ebfeb1f712ebc6f1", + "name": [{ + "value": "Example University", + "lang": "en" + }, { + "value": "Exemple d'Université", + "lang": "fr" + }] + } + } +} \ No newline at end of file