From cb0556463b58373afe94eed501eaeaff64764bf8 Mon Sep 17 00:00:00 2001 From: Gabe Date: Mon, 14 Nov 2022 17:06:54 -0800 Subject: [PATCH] Add JSON Schema for Verifiable Credentials (#977) * first draft of schema * tests and readme * pr comments * add proof chain and proof arrays * add vp schema and tests * remove unused dep * update readme * Update schema/verifiable-presentation/verifiable-presentation-schema-test.js Co-authored-by: Ted Thibodeau Jr * Update schema/README.md Co-authored-by: Ted Thibodeau Jr * Update schema/README.md Co-authored-by: Ted Thibodeau Jr Co-authored-by: Ted Thibodeau Jr --- .gitignore | 2 +- package.json | 11 +- schema/README.md | 25 ++ schema/__fixtures__/ajv.js | 3 + schema/verifiable-credential/example-1.json | 30 ++ schema/verifiable-credential/example-11.json | 24 ++ schema/verifiable-credential/example-12.json | 18 ++ schema/verifiable-credential/example-13.json | 21 ++ schema/verifiable-credential/example-18.json | 21 ++ schema/verifiable-credential/example-20.json | 21 ++ schema/verifiable-credential/example-21.json | 28 ++ schema/verifiable-credential/example-23.json | 26 ++ schema/verifiable-credential/example-4.json | 17 + .../verifiable-credential-schema-test.js | 92 ++++++ .../verifiable-credential-schema.json | 297 ++++++++++++++++++ schema/verifiable-presentation/example-2.json | 48 +++ .../verifiable-presentation/example-25.json | 40 +++ .../verifiable-presentation/example-45.json | 71 +++++ .../verifiable-presentation-schema-test.js | 45 +++ .../verifiable-presentation-schema.json | 144 +++++++++ 20 files changed, 982 insertions(+), 2 deletions(-) create mode 100644 schema/README.md create mode 100644 schema/__fixtures__/ajv.js create mode 100644 schema/verifiable-credential/example-1.json create mode 100644 schema/verifiable-credential/example-11.json create mode 100644 schema/verifiable-credential/example-12.json create mode 100644 schema/verifiable-credential/example-13.json create mode 100644 schema/verifiable-credential/example-18.json create mode 100644 schema/verifiable-credential/example-20.json create mode 100644 schema/verifiable-credential/example-21.json create mode 100644 schema/verifiable-credential/example-23.json create mode 100644 schema/verifiable-credential/example-4.json create mode 100644 schema/verifiable-credential/verifiable-credential-schema-test.js create mode 100644 schema/verifiable-credential/verifiable-credential-schema.json create mode 100644 schema/verifiable-presentation/example-2.json create mode 100644 schema/verifiable-presentation/example-25.json create mode 100644 schema/verifiable-presentation/example-45.json create mode 100644 schema/verifiable-presentation/verifiable-presentation-schema-test.js create mode 100644 schema/verifiable-presentation/verifiable-presentation-schema.json diff --git a/.gitignore b/.gitignore index 61bb834fd..4e517f831 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,4 @@ **/node_modules **/.DS_Store **/.vscode - +**/package-lock.json \ No newline at end of file diff --git a/package.json b/package.json index 889a1a818..eb1a18829 100644 --- a/package.json +++ b/package.json @@ -14,5 +14,14 @@ "bugs": { "url": "https://github.com/w3c/vc-data-model/issues" }, - "homepage": "https://github.com/w3c/vc-data-model" + "homepage": "https://github.com/w3c/vc-data-model", + "dependencies": { + "ajv": "^8.11.0", + "assert": "^2.0.0", + "fs": "^0.0.1-security", + "mocha": "^10.1.0" + }, + "scripts": { + "test-schema": "mocha 'schema/**/*.js'" + } } diff --git a/schema/README.md b/schema/README.md new file mode 100644 index 000000000..0511c03d8 --- /dev/null +++ b/schema/README.md @@ -0,0 +1,25 @@ +# Verifiable Credentials JSON Schema + +This directory defines [JSON Schemas](https://json-schema.org/) for the Verifiable Credentials Data Model. + +Currently, this implementation makes use of **JSON Schema version 2020-12**: +- [JSON Schema Core](https://json-schema.org/draft/2020-12/json-schema-core.html) +- [JSON Schema Validation](https://json-schema.org/draft/2020-12/json-schema-validation.html) +- [Relative JSON Pointers](https://json-schema.org/draft/2020-12/relative-json-pointer.html) + +# Schemas + +There are two schemas provided, each with associated tests: + +- [Verifiable Credential JSON Schema](verifiable-credential/verifiable-credential-schema.json) +- [Verifiable Presentation JSON Schema](verifiable-presentation/verifiable-presentation-schema.json) + +# Tests + +A limited set of tests against examples in the specification are included. To run these tests, you can use the following command, after `npm i`: + +```bash +npm run test-schemas +``` + +The tests rely on examples featured in the spec. Currently these examples have been copy-pasted, and there is no guarantee they are in sync with the spec. This should be improved! \ No newline at end of file diff --git a/schema/__fixtures__/ajv.js b/schema/__fixtures__/ajv.js new file mode 100644 index 000000000..fef74c420 --- /dev/null +++ b/schema/__fixtures__/ajv.js @@ -0,0 +1,3 @@ +const Ajv2020 = require("ajv/dist/2020"); +const ajv = new Ajv2020({allErrors: true}); +module.exports = ajv; \ No newline at end of file diff --git a/schema/verifiable-credential/example-1.json b/schema/verifiable-credential/example-1.json new file mode 100644 index 000000000..2192135e2 --- /dev/null +++ b/schema/verifiable-credential/example-1.json @@ -0,0 +1,30 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/1872", + "type": ["VerifiableCredential", "AlumniCredential"], + "issuer": "https://example.edu/issuers/565049", + "issuanceDate": "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" + }] + } + }, + "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/schema/verifiable-credential/example-11.json b/schema/verifiable-credential/example-11.json new file mode 100644 index 000000000..5151745a7 --- /dev/null +++ b/schema/verifiable-credential/example-11.json @@ -0,0 +1,24 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.gov/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu", + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + }, + "proof": { + "type": "Ed25519Signature2020", + "created": "2021-11-13T18:19:39Z", + "verificationMethod": "https://example.edu/issuers/14#key-1", + "proofPurpose": "assertionMethod", + "proofValue": "z58DAdFfa9SkqZMVPxAQpic7ndSayn1PzZs6ZjWp1CktyGesjuTSwRdoWhAfGFCF5bppETSTojQCrfFPP2oumHKtz" + } +} \ No newline at end of file diff --git a/schema/verifiable-credential/example-12.json b/schema/verifiable-credential/example-12.json new file mode 100644 index 000000000..14d88a7bd --- /dev/null +++ b/schema/verifiable-credential/example-12.json @@ -0,0 +1,18 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu/issuers/14", + "issuanceDate": "2010-01-01T19:23:24Z", + "expirationDate": "2020-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + } +} \ No newline at end of file diff --git a/schema/verifiable-credential/example-13.json b/schema/verifiable-credential/example-13.json new file mode 100644 index 000000000..75a453ebd --- /dev/null +++ b/schema/verifiable-credential/example-13.json @@ -0,0 +1,21 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu/issuers/14", + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + }, + "credentialStatus": { + "id": "https://example.edu/status/24", + "type": "CredentialStatusList2017" + } +} \ No newline at end of file diff --git a/schema/verifiable-credential/example-18.json b/schema/verifiable-credential/example-18.json new file mode 100644 index 000000000..41d78026c --- /dev/null +++ b/schema/verifiable-credential/example-18.json @@ -0,0 +1,21 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu/issuers/14", + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + }, + "credentialSchema": { + "id": "https://example.org/examples/degree.json", + "type": "JsonSchemaValidator2018" + } +} \ No newline at end of file diff --git a/schema/verifiable-credential/example-20.json b/schema/verifiable-credential/example-20.json new file mode 100644 index 000000000..560149036 --- /dev/null +++ b/schema/verifiable-credential/example-20.json @@ -0,0 +1,21 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu/issuers/14", + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + }, + "refreshService": { + "id": "https://example.edu/refresh/3732", + "type": "ManualRefreshService2018" + } +} \ No newline at end of file diff --git a/schema/verifiable-credential/example-21.json b/schema/verifiable-credential/example-21.json new file mode 100644 index 000000000..d1b8b029f --- /dev/null +++ b/schema/verifiable-credential/example-21.json @@ -0,0 +1,28 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu/issuers/14", + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + }, + "termsOfUse": [{ + "type": "IssuerPolicy", + "id": "http://example.com/policies/credential/4", + "profile": "http://example.com/profiles/credential", + "prohibition": [{ + "assigner": "https://example.edu/issuers/14", + "assignee": "AllVerifiers", + "target": "http://example.edu/credentials/3732", + "action": ["Archival"] + }] + }] +} \ No newline at end of file diff --git a/schema/verifiable-credential/example-23.json b/schema/verifiable-credential/example-23.json new file mode 100644 index 000000000..6de1e942f --- /dev/null +++ b/schema/verifiable-credential/example-23.json @@ -0,0 +1,26 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu/issuers/14", + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + }, + "evidence": [{ + "id": "https://example.edu/evidence/f2aeec97-fc0d-42bf-8ca7-0548192d4231", + "type": ["DocumentVerification"], + "verifier": "https://example.edu/issuers/14", + "evidenceDocument": "DriversLicense", + "subjectPresence": "Physical", + "documentPresence": "Physical", + "licenseNumber": "123AB4567" + }] +} \ No newline at end of file diff --git a/schema/verifiable-credential/example-4.json b/schema/verifiable-credential/example-4.json new file mode 100644 index 000000000..5b8c7e07d --- /dev/null +++ b/schema/verifiable-credential/example-4.json @@ -0,0 +1,17 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/3732", + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "issuer": "https://example.edu/issuers/565049", + "issuanceDate": "2010-01-01T00:00:00Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "degree": { + "type": "BachelorDegree", + "name": "Bachelor of Science and Arts" + } + } +} \ No newline at end of file diff --git a/schema/verifiable-credential/verifiable-credential-schema-test.js b/schema/verifiable-credential/verifiable-credential-schema-test.js new file mode 100644 index 000000000..b94168bd8 --- /dev/null +++ b/schema/verifiable-credential/verifiable-credential-schema-test.js @@ -0,0 +1,92 @@ +const fs = require('fs'); +const assert = require('assert'); +const ajv = require('../__fixtures__/ajv'); + +const verifiableCredentialSchema = "/verifiable-credential-schema.json"; +const schema = JSON.parse(fs.readFileSync(__dirname + verifiableCredentialSchema)); + +describe('Verifiable Credential', function () { + describe('JSON Schema', function () { + + it('should validate example 1 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-1.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + it('should validate example 4 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-4.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + it('should validate example 11 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-11.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + it('should validate example 12 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-12.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + it('should validate example 13 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-13.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + it('should validate example 18 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-18.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + it('should validate example 20 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-20.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + it('should validate example 21 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-21.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + it('should validate example 23 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-23.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + }); +}); diff --git a/schema/verifiable-credential/verifiable-credential-schema.json b/schema/verifiable-credential/verifiable-credential-schema.json new file mode 100644 index 000000000..6ecafb6b8 --- /dev/null +++ b/schema/verifiable-credential/verifiable-credential-schema.json @@ -0,0 +1,297 @@ +{ + "$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 + }, + "refreshService": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type" + } + }, + "required": [ + "id", + "type" + ], + "additionalProperties": true + }, + "termsOfUse": { + "type": "object", + "properties": { + "type": { + "$ref": "#/$defs/type" + }, + "id": { + "type": "string" + } + }, + "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", + "items": { + "type": "string" + }, + "contains": { + "const": "https://www.w3.org/2018/credentials/v1" + }, + "minItems": 1 + }, + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type", + "contains": { + "const": "VerifiableCredential" + } + }, + "issuer": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ], + "additionalProperties": true + } + ] + }, + "issuanceDate": { + "type": "string" + }, + "credentialSubject": { + "oneOf": [ + { + "$ref": "#/$defs/credentialSubject" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/credentialSubject" + }, + "minItems": 1 + } + ] + }, + "expirationDate": { + "type": "string" + }, + "credentialStatus": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type" + } + }, + "required": [ + "id", + "type" + ], + "additionalProperties": true + }, + "credentialSchema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type" + } + }, + "required": [ + "id", + "type" + ], + "additionalProperties": true + }, + "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", + "type", + "issuer", + "issuanceDate", + "credentialSubject" + ], + "additionalProperties": true +} \ No newline at end of file diff --git a/schema/verifiable-presentation/example-2.json b/schema/verifiable-presentation/example-2.json new file mode 100644 index 000000000..6dddcfa3a --- /dev/null +++ b/schema/verifiable-presentation/example-2.json @@ -0,0 +1,48 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "type": "VerifiablePresentation", + + "verifiableCredential": [{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://example.edu/credentials/1872", + "type": ["VerifiableCredential", "AlumniCredential"], + "issuer": "https://example.edu/issuers/565049", + "issuanceDate": "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" + }] + } + }, + "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" + } + }], + + "proof": { + "type": "RsaSignature2018", + "created": "2018-09-14T21:19:10Z", + "proofPurpose": "authentication", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21#keys-1", + "challenge": "1f44d55f-f161-4938-a659-f8026467f126", + "domain": "4jt78h47fh47", + "jws": "eyJhbGciOiJSUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..kTCYt5XsITJX1CxPCT8yAV-TVIw5WEuts01mq-pQy7UJiN5mgREEMGlv50aqzpqh4Qq_PbChOMqsLfRoPsnsgxD-WUcX16dUOqV0G_zS245-kronKb78cPktb3rk-BuQy72IFLN25DYuNzVBAh4vGHSrQyHUGlcTwLtjPAnKb78" + } +} \ No newline at end of file diff --git a/schema/verifiable-presentation/example-25.json b/schema/verifiable-presentation/example-25.json new file mode 100644 index 000000000..b775f6748 --- /dev/null +++ b/schema/verifiable-presentation/example-25.json @@ -0,0 +1,40 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "type": "VerifiablePresentation", + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "type": ["VerifiableCredential", "UniversityDegreeCredential"], + "credentialSchema": { + "id": "did:example:cdf:35LB7w9ueWbagPL94T9bMLtyXDj9pX5o", + "type": "did:example:schema:22KpkXgecryx9k7N6XN1QoN3gXwBkSU8SfyyYQG" + }, + "issuer": "did:example:Wz4eUg7SetGfaUVCn8U9d62oDYrUJLuUtcy619", + "issuanceDate": "2017-12-05T14:27:42Z", + "credentialSubject": { + "degreeType": "BachelorDegree", + "degreeSchool": "College of Engineering" + }, + "proof": { + "type": "AnonCredDerivedCredentialv1", + "primaryProof": "cg7wLNSi48K5qNyAVMwdYqVHSMv1Ur8i...Fg2ZvWF6zGvcSAsym2sgSk737", + "nonRevocationProof": "mu6fg24MfJPU1HvSXsf3ybzKARib4WxG...RSce53M6UwQCxYshCuS3d2h", + "created": "2018-06-17T10:03:48Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21" + } + }], + "proof": { + "type": "AnonCredPresentationProofv1", + "proofValue": "DgYdYMUYHURJLD7xdnWRinqWCEY5u5fK...j915Lt3hMzLHoPiPQ9sSVfRrs1D", + "created": "2018-06-17T10:03:48Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21" + } +} \ No newline at end of file diff --git a/schema/verifiable-presentation/example-45.json b/schema/verifiable-presentation/example-45.json new file mode 100644 index 000000000..ba4c0b6d6 --- /dev/null +++ b/schema/verifiable-presentation/example-45.json @@ -0,0 +1,71 @@ +{ + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "https://example.com/VP/0987654321", + "type": ["VerifiablePresentation"], + "verifiableCredential": [ + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "http://pharma.example.com/credentials/3732", + "type": ["VerifiableCredential", "PrescriptionCredential"], + "issuer": "https://pharma.example.com/issuer/4", + "issuanceDate": "2010-01-01T19:23:24Z", + "credentialSubject": { + "id": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "prescription": { + "name": "prescription-1" + } + }, + "credentialStatus": { + "id": "https://pharma.example.com/credentials/status/3#94567", + "type": "RevocationList2020Status", + "revocationListIndex": "94567", + "revocationListCredential": "https://pharma.example.com/credentials/status/3" + }, + "proof": { + "type": "RsaSignature2018", + "created": "2018-06-18T21:19:10Z", + "proofPurpose": "authentication", + "verificationMethod": "did:example:76e12ec21ebhyu1f712ebc6f1z2/keys/2", + "challenge": "c0ae1c8e-c7e7-469f-b252-86e6a0e7387e", + "jws": "BavEll0/I1..W3JT24=" + } + }, + { + "@context": [ + "https://www.w3.org/2018/credentials/v1", + "https://www.w3.org/2018/credentials/examples/v1" + ], + "id": "https://example.com/VC/123456789", + "type": ["VerifiableCredential", "PrescriptionCredential"], + "issuer": "did:example:ebfeb1f712ebc6f1c276e12ec21", + "issuanceDate": "2010-01-03T19:53:24Z", + "credentialSubject": { + "id": "did:example:76e12ec21ebhyu1f712ebc6f1z2", + "prescription": { + "name": "prescription-1" + } + }, + "proof": { + "type": "RsaSignature2018", + "created": "2018-06-17T10:03:48Z", + "proofPurpose": "assertionMethod", + "jws": "pYw8XNi1..Cky6Ed=", + "verificationMethod": "did:example:ebfeb1f712ebc6f1c276e12ec21/keys/234" + } + } + ], + "proof": [{ + "type": "RsaSignature2018", + "created": "2018-06-18T21:19:10Z", + "proofPurpose": "authentication", + "verificationMethod": "did:example:76e12ec21ebhyu1f712ebc6f1z2/keys/2", + "challenge": "c0ae1c8e-c7e7-469f-b252-86e6a0e7387e", + "jws": "BavEll0/I1..W3JT24=" + }] +} \ No newline at end of file diff --git a/schema/verifiable-presentation/verifiable-presentation-schema-test.js b/schema/verifiable-presentation/verifiable-presentation-schema-test.js new file mode 100644 index 000000000..cc2e20397 --- /dev/null +++ b/schema/verifiable-presentation/verifiable-presentation-schema-test.js @@ -0,0 +1,45 @@ +const fs = require('fs'); +const assert = require('assert'); +const ajv = require('../__fixtures__/ajv'); + +const verifiablePresentationSchema = "/verifiable-presentation-schema.json"; +const schema = JSON.parse(fs.readFileSync(__dirname + verifiablePresentationSchema)); + +describe('Verifiable Presentation', function () { + describe('JSON Schema', function () { + + it('should validate example 2 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-2.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + // Note: this example fails because the dependent VC is not valid; + // it is missing an issuanceDate. Both the VP and VC proofs are missing + // created, proofPurpose, and verificationMethod fields. The example has + // been modified to get the test to pass. + it('should validate example 25 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-25.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + // Note: this example fails because the "prescription" fields are empty and one "proof" + // field is empty. The example has been modified to get the test to pass. + it('should validate example 45 using JSON Schema 2020-12 ', function () { + const data = JSON.parse(fs.readFileSync(__dirname + '/example-45.json')); + const validate = ajv.compile(schema); + const valid = validate(data); + + assert.equal(null, validate.errors); + assert.equal(true, valid); + }); + + }); +}); diff --git a/schema/verifiable-presentation/verifiable-presentation-schema.json b/schema/verifiable-presentation/verifiable-presentation-schema.json new file mode 100644 index 000000000..bb6677f2a --- /dev/null +++ b/schema/verifiable-presentation/verifiable-presentation-schema.json @@ -0,0 +1,144 @@ +{ + "$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": { + "type": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "minItems": 1 + } + ] + }, + "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", + "items": { + "type": "string" + }, + "contains": { + "const": "https://www.w3.org/2018/credentials/v1" + }, + "minItems": 1 + }, + "id": { + "type": "string" + }, + "type": { + "$ref": "#/$defs/type", + "contains": { + "const": "VerifiablePresentation" + } + }, + "verifiableCredential": { + "oneOf": [ + { + "$ref": "https://www.w3.org/2022/credentials/v2/verifiable-credential-schema.json" + }, + { + "type": "array", + "items": { + "$ref": "https://www.w3.org/2022/credentials/v2/verifiable-credential-schema.json" + }, + "minItems": 1 + } + ] + }, + "proof": { + "oneOf": [ + { + "$ref": "#/$defs/proof" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/proof" + }, + "minItems": 1 + } + ] + }, + "proofChain": { + "$ref": "#/$defs/proofChain" + } + }, + "required": [ + "@context", + "type" + ], + "additionalProperties": true +} \ No newline at end of file