diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fe8bd5..88a01e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.1.0 22 Sept 2023: + +- Add in VP and VC verification for did:key to ensure VC did:key matches with VP did:key + ## 0.0.8 11 Sep 2023: - Add getExpirationStatus function diff --git a/__tests__/index.test.js b/__tests__/index.test.js index fc1e39e..4ab4b26 100644 --- a/__tests__/index.test.js +++ b/__tests__/index.test.js @@ -9,6 +9,7 @@ const invalidVC = require("./stub/invalidVC.json"); const revokedVC = require("./stub/revokedVC.json"); const verifyresult = require("./stub/result.json"); const signedVP2018 = require("./stub/signedVP2018.json"); +const didKeyInvalidVP2018 = require("./stub/didKeyInvalidVP2018.json"); const invalidVP = require("./stub/invalidVP.json"); const verifyPresentationResult2018 = require("./stub/resultPresentation2018.json"); const signedSDVC = require("./stub/signedSelectiveDisclosedVC.json"); @@ -97,13 +98,23 @@ describe("Test VC verifier", () => { expect(result.verified).toStrictEqual(true); }, 5000); - it("should validate expirationStatus as expired", async () => { + it("should validate expirationStatus as expired (true)", async () => { const result = MyInfoVcVerifier.getExpirationStatus(expiredVC); expect(result).toStrictEqual(true); }); - it("should validate expirationStatus as not expired", async () => { + it("should validate expirationStatus as not expired (false)", async () => { const result = MyInfoVcVerifier.getExpirationStatus(notExpiredVC); expect(result).toStrictEqual(false); }); + + it("should verify did:key verificationStatus as matches (true)", async () => { + const result = MyInfoVcVerifier.verifyPresentationAndCredentialDidKey(signedVP2018); + expect(result).toStrictEqual(true); + }); + + it("should verify did:key verificationStatus as does not matches (false)", async () => { + const result = MyInfoVcVerifier.verifyPresentationAndCredentialDidKey(didKeyInvalidVP2018); + expect(result).toStrictEqual(false); + }); }); diff --git a/__tests__/stub/didKeyInvalidVP2018.json b/__tests__/stub/didKeyInvalidVP2018.json new file mode 100644 index 0000000..0a94099 --- /dev/null +++ b/__tests__/stub/didKeyInvalidVP2018.json @@ -0,0 +1,99 @@ +{ + "@context": ["https://www.w3.org/2018/credentials/v1"], + "id": "did:key:z6MkfJVf63AcEoiL9CJJ2r76eZL3dS9QugWk5cDWCYp9raYx#vp-15cfeb06-c893-4a88-b2fd-2c6122db69d7", + "type": ["VerifiablePresentation"], + "holder": "did:key:z6MkfJVf63AcEoiL9CJJ2r76eZL3dS9QugWk5cDWCYp9raYx", + "verifiableCredential": [ + { + "@context": [ + "https://w3id.org/security/bbs/v1", + "https://www.w3.org/2018/credentials/v1", + "https://stg.issuer.myinfo.gov.sg/myinfo/schema/age/v1", + "https://w3id.org/vc/status-list/2021/v1" + ], + "id": "https://stg.issuer.myinfo.gov.sg/myinfo202304-01/credentials/b2086515-619c-427c-b707-84f238764eac", + "type": ["VerifiableCredential", "AgeCredential"], + "issuer": "did:web:stg.issuer.myinfo.gov.sg:myinfo202304-01", + "credentialSubject": { + "AtLeast18YearsOld": true, + "AtLeast21YearsOld": true, + "type": ["Age"], + "id": "did:key:z6MkisZuWTqwmE4XxnSRkGibr5hkLCqVkdvcpMpTRViLtJ9s" + }, + "name": "Age", + "description": "Age Over Indicators", + "expirationDate": "2025-08-21T06:51:34Z", + "credentialStatus": { + "id": "https://stg.issuer.myinfo.gov.sg/myinfo202304-01/status/1#10267", + "type": "StatusList2021Entry", + "statusListIndex": "10267", + "statusListCredential": "https://stg.issuer.myinfo.gov.sg/myinfo202304-01/status/1" + }, + "issuanceDate": "2023-08-21T07:05:15Z", + "proof": { + "created": "2023-08-21T07:05:15Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:web:stg.issuer.myinfo.gov.sg:myinfo202304-01#21402a47-6416-4b95-b180-dff0e1f44d67", + "type": "BbsBlsSignatureProof2020", + "nonce": "KIr/YTYd9HGtHoHe4Nms3PjhxtjiW+xhVYrTcDgOqoc=", + "proofValue": "ABMH//+jbjXQAjbCiOj5Oh6lxFnP45WC4N675Xe8abpOzdN5h1zhxkFlNbas965ep5fqHIG3JGWmlaAe7jL9RqNHNSi5vPaOcnjIu0zHZA4dT9z98viovnPQG8GRoQWNCQllxS+lzW0ucRHG9ruNApHzOv+ESkoPU4/tBbZtm9HPQ8u00N/edXHC7HtkVSJzXSfFwe4AAAB0s+hWBhiH7v3AMi36Is6MvOHz55ZK5l9c8cjbkBqAXl0qt+mh5IACj7wlAeMgCNc8AAAAAhyzTjmFL9cRSLUkXsBAAGbdpZrQqDC1ehc35uonjj8SQv/JgYhlzO/c3e9JMWMj1GuMESzO/H2Slq22d/jFxwyPgKGEQ4I7S7sDrELuIsj3oINYz2e10uOBfDeSPCeAxGhcyG3Myy1mxRFwH50beHUAAAACPPmYj1uOXKtJAlu+FIt3J6q3ibYJ9+rcFXVPapwI8lQqPF7cpSwAE6A9kZbazXH46NkUV1KKec4Uz2l2wLagAg==" + } + }, + { + "@context": [ + "https://w3id.org/security/bbs/v1", + "https://www.w3.org/2018/credentials/v1", + "https://stg.issuer.myinfo.gov.sg/myinfo/schema/personal/v1", + "https://w3id.org/vc/status-list/2021/v1" + ], + "id": "https://stg.issuer.myinfo.gov.sg/myinfo202304-01/credentials/0ba368dd-f58a-4455-846f-365382ade416", + "type": ["VerifiableCredential", "PersonalCredential"], + "issuer": "did:web:stg.issuer.myinfo.gov.sg:myinfo202304-01", + "credentialSubject": { + "uinfin": "S8703396A", + "name": "User S8703396A", + "aliasname": "", + "hanyupinyinname": "", + "hanyupinyinaliasname": "", + "marriedname": "", + "sex": "MALE", + "race": "AMERICAN", + "dob": "1990-04-19", + "residentialstatus": "CITIZEN", + "nationality": "GUINEAN", + "birthcountry": "UNITED STATES", + "regadd": { + "country": "SINGAPORE", + "postal": "609774" + }, + "type": ["Personal"], + "id": "did:key:z6MkisZuWTqwmE4XxnSRkGibr5hkLCqVkdvcpMpTRViLtJ9s" + }, + "name": "Personal", + "description": "Basic Details of Personal Profile", + "expirationDate": "2025-08-21T06:51:34Z", + "credentialStatus": { + "id": "https://stg.issuer.myinfo.gov.sg/myinfo202304-01/status/1#10268", + "type": "StatusList2021Entry", + "statusListIndex": "10268", + "statusListCredential": "https://stg.issuer.myinfo.gov.sg/myinfo202304-01/status/1" + }, + "issuanceDate": "2023-08-21T07:05:20Z", + "proof": { + "created": "2023-08-21T07:05:20Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:web:stg.issuer.myinfo.gov.sg:myinfo202304-01#21402a47-6416-4b95-b180-dff0e1f44d67", + "type": "BbsBlsSignatureProof2020", + "nonce": "MfQWYAz9Ha+3ygEUmhlOkmdnAHyiskxvTI7Fc2fT2ac=", + "proofValue": "ACoAp//8fP+Y9rrEm2me6HlQOHtU7A1g6cR6Bvx31BXV4fbHtYFhhN1fTszqrlGwvERxYUTW8+eMJxa+7lmdfO3qXchUAQDHM/yiw8Tmy6bi4xnr/2WJCgmf/2GXWSEiOHrRRD0xgD2qlej+qs/X9v+VJ1vJxQMYaXUmTel5Zl9IZ8M3/F/oJ9tY+41MBa1gNkF/wKljshwAAAB0qkEdjkws3IUCtxdrNszurU0fzkoL2ataW/reLCMLHjtEqAuFEPO4JHOCPfYdlm0DAAAAAkp9+vIiTC4uvkIEIHHhVf2KZk1jtbo+U0poBHlOwzoNESJH1Q+70PdcEjIEWVfUGEBz0rAQinE6ueI6OQ10AjSRJvL/zjzpfzX7nLvXsK8UfmHkR2HHCaVF6kSRSsdkzNNycBdlenGE1Wy7jTac7NUAAAAMYJ2Y1euh+HilfjQjgl3gMKHAQBUmUg6Dy6TUaBYab8UP1/LGyOwRWveiPZcJJadp5sIeZsfrAZZ2hSVL6DMDoWsLqKn2BkKJpUhSgaMtPcwdE7PhGst/GPWk/kTgJzNfHnI5w3xFUM0mvhM8guSKY+xK9Bs/rTXTItiM/+k8K9ZS3jmsE3JhgKs+xmLQYX29AeVUJGHOLhWE2yqXnCUE/lJ2te5o2FvDh9VPm/4EwpZY4R0SF1esqW0MlCjle657Q6AvK+sGO73ECxxilTP3SCXnLFSMrn/2ov1c6mA9iL5Yo/p8CwP731mTLd+3vUVZTreK87WVmlQuAMQA5Lm3PyPGDEeAIBlbpdVyZs6w8hlOAYzMHXj5wyrNNf5Pbz/lXNxtZhH99jAdGyL+SQU0RiUiz7mV9kTlcLRQxqocQhhwevc1PkF8O2Y8gdLlGXR+NiHEOX39hvY0E2BccpnnmzOnTNUZOGCORU7CsZDz1YdkKCrFpFy7w6blF5D99saq" + } + } + ], + "proof": { + "type": "Ed25519Signature2018", + "created": "2023-08-22T06:47:00Z", + "proofPurpose": "assertionMethod", + "verificationMethod": "did:key:z6MkfJVf63AcEoiL9CJJ2r76eZL3dS9QugWk5cDWCYp9raYx#z6MkfJVf63AcEoiL9CJJ2r76eZL3dS9QugWk5cDWCYp9raYx", + "jws": "eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJhbGciOiJFZERTQSJ9..pTydH1ytitQ_FiblmcgsqZC0vxjNp-GpQNbruCO9d9dPC1xUGYoWjitAv0St8zyJgmJ_aFcvXnGuAANIaZJGCw" + } +} diff --git a/index.js b/index.js index c5e9885..3942c73 100644 --- a/index.js +++ b/index.js @@ -220,6 +220,12 @@ MyInfoVcVerifier.verifyPresentation = async function (presentation, customDocume let documentLoader = await getDocumentLoader(customDocuments); let suite = [new jsonldSignatures.suites.Ed25519Signature2018()]; + if (!this.verifyPresentationAndCredentialDidKey(presentation)) { + return { + verified: false, + }; + } + const result = await jsonldSignatures.verify(presentation, { suite: suite, purpose: new jsonldSignatures.purposes.AssertionProofPurpose(), @@ -228,6 +234,24 @@ MyInfoVcVerifier.verifyPresentation = async function (presentation, customDocume return result; }; +/** + * [Verify Presentation and Credential Did Key] + * @param {Object} Verifiable Presentation object [signed verifiable presentation] + * @return {Boolean} [Did Key Verification Status] TRUE - did:key matches, FALSE - did:key does not match + */ +MyInfoVcVerifier.verifyPresentationAndCredentialDidKey = (presentation) => { + let vpVerificationMethod = presentation.proof.verificationMethod; + let vpDidKey = vpVerificationMethod.split("#")[0]; + + for (let vc of presentation.verifiableCredential) { + if (vc.credentialSubject.id !== vpDidKey) { + return false; + } + } + + return true; +}; + /** * [Check Revoke Status] * @param {Object} encoded [the verified encoded list] diff --git a/package.json b/package.json index f875934..4257db4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "myinfo-vc-verifier", - "version": "0.0.8", + "version": "0.1.0", "description": "This package provides the functionality to verify a verifiable credential using @mattrglobal jsonld-signatures-bbs.", "main": "index.js", "scripts": {