diff --git a/.gitignore b/.gitignore index 7f3cce3..4d2e04a 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,6 @@ lerna-debug.log* !.vscode/extensions.json output/*.svg -output/*.png \ No newline at end of file +output/*.png + +test/fixtures diff --git a/package.json b/package.json index 9381f52..1047f0e 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@auth0/mdl": "^2.0.1", "@sd-jwt/core": "^0.10.0", + "@sd-jwt/sd-jwt-vc": "^0.10.0", "ajv": "^8.17.1", "asn1js": "^3.0.5", "axios": "^1.7.9", diff --git a/src/credential-verifiers/SDJWTVCVerifier.test.ts b/src/credential-verifiers/SDJWTVCVerifier.test.ts index 1af6167..d1cac5f 100644 --- a/src/credential-verifiers/SDJWTVCVerifier.test.ts +++ b/src/credential-verifiers/SDJWTVCVerifier.test.ts @@ -3,55 +3,58 @@ import { Context } from "../interfaces"; import { SDJWTVCVerifier } from "./SDJWTVCVerifier"; import { PublicKeyResolverEngine } from "../PublicKeyResolverEngine"; import { CredentialVerificationError } from "../error"; +import { sdJwtFixture } from '../../test/fixtures' const wrongFormatCredential = `omppc3N1ZXJBdXRohEOhASahGCGCWQJ4MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf_7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2_J0WVeghyw-mIwDAYDVR0TAQH_BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr-ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW_U_5S5vAEC5XxcOanusOBroBbVZAn0wggJ5MIICIKADAgECAhQHkT1BVm2ZRhwO0KMoH8fdVC_vaDAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDY0ODA5WhcNMzQwNTI5MDY0ODA5WjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARgbN3AUOdzv4qfmJsC8I4zyR7vtVDGp8xzBkvwhogD5YJE5wJ-Zj-CIf3aoyu7mn-TI6K8TREL8ht0w428OhTJo2YwZDAdBgNVHQ4EFgQU1FYYwIk46A5YhBjJdmK_q7vFkL4wHwYDVR0jBBgwFoAU1FYYwIk46A5YhBjJdmK_q7vFkL4wEgYDVR0TAQH_BAgwBgEB_wIBADAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDRwAwRAIgYSbvCRkoe39q1vgx0WddbrKufAxRPa7XfqB22XXRjqECIG5MWq9Vi2HWtvHMI_TFZkeZAr2RXLGfwY99fbsQjPOzWQS62BhZBLWnZnN0YXR1c6Frc3RhdHVzX2xpc3SiY2lkeBhsY3VyaXhWaHR0cHM6Ly9kZW1vLnBpZC1pc3N1ZXIuYnVuZGVzZHJ1Y2tlcmVpLmRlL3N0YXR1cy84ODc5M2MwMy0xNmFkLTQ0NjgtYmVmNy1jMDgzZDM4YWUyMTlnZG9jVHlwZXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMWd2ZXJzaW9uYzEuMGx2YWxpZGl0eUluZm-jZnNpZ25lZMB0MjAyNS0wMi0xOFQxNDoxMjowNFppdmFsaWRGcm9twHQyMDI1LTAyLTE4VDE0OjEyOjA0Wmp2YWxpZFVudGlswHQyMDI1LTAzLTA0VDE0OjEyOjA0Wmx2YWx1ZURpZ2VzdHOhd2V1LmV1cm9wYS5lYy5ldWRpLnBpZC4xtgBYIKuGxnFMGhNio5-VUJKePlkmw33mloMA9fgqUR0ynOoJAVggWxNyUrVxTPW2riSGxx_U_irluD-vcJIOGGrafGo6JpwCWCDKOCdlxlbeX7mztFkzrM7MsZHs3gEyrmC79X3N2VpxkgNYICmI6iaQPBePM7fzBXqPyX5Gr-wNnWNCNb7wDUz4VDIRBFggfCuu8bFboi9BiRPsM447Ncg9A7K7A28iTEjVy9fmjBIFWCC6z1AlQM8ttJfuIQtPYlurlamh3MvAbSaQoUzAn-9L9gZYIKD1mVbZ5zb-_sp_E6vZCQ_U2QAQVNtbWAznR4xUm6LoB1ggWAn0OSPMM-m8NbgBZ-D6qLV0BEVeSnR4DIsUPUOZDbsIWCDyTDBH9XjK_JIq_W7d19UpmMq1pd1CjrmhfIHsctg3gwlYIK7ejRc3g-pfNGM0WHv4Oh1jfshl03Jvm3cxKHFnIIXmClggjPVDgZmiJEpnM6Zo_mzUQAbW5M6QZuRH43L6BqVeT7wLWCCSVNDu2CjnRkbC7_6m6-G6h8dTDWvlmGz0WD-MUCGERwxYIDpAXdFHgnACMgICXQpJi9nzBDRjsJ8bY1htM9GtgZlKDVggvhyWJk8WGQgokFghnd9DyZKyo8b6VrfAX8WTB0vH1QkOWCBLJFY_nbKL1x-5fbJCqS1IgEn_uMm9NJm2vqorCWwwPg9YIJIg7rTS_E3HAYjcjdV6WSpgZuXa8IKo7f5aC9ibPXQzEFggc_BlS8FdmjVtSqXrA2Xh58naoO0XdTbwclGo9itNTIERWCDzIo5muAIWaawEG69bUPG4mI4pEB5dUhadaUeMUEuwIhJYIEALsAqnwl3T1nC7YtOeDj-7OEHlmcwhCZjY2Qgsr2vCE1ggwG6In0GuGqO1isPXfh2EA7-mi18JAhfumCyQUA5FpYYUWCAL6kBisfFYUIU06t2d0UeqElM-c49VrVqfgYYSIx2JpRVYICYx93c95xCPFdhE03ZlReMnLGSjT_SJgEBMeErv0VlXbWRldmljZUtleUluZm-haWRldmljZUtleaQBAiABIVgganiJYJ0goJBbFzWZ52BDtTvTP1Fqb6k80C4UBl6JrFwiWCCWf2o4RIOTRI_UGubc0rCyIDo-o_LYRzYRnWzos3gcSm9kaWdlc3RBbGdvcml0aG1nU0hBLTI1NlhAcBP9-i1suGc_TnH7z4Mp8jFAz2Q__4w7Ju7dDG93XWfCE15E15WYaXUnkYY80tStLInk7nEi6IqEPHJPUyWiyGpuYW1lU3BhY2VzoXdldS5ldXJvcGEuZWMuZXVkaS5waWQuMZbYGFhRpGZyYW5kb21Q6lwO6tOJcjKhPDMrRPrRFGhkaWdlc3RJRABsZWxlbWVudFZhbHVlGDxxZWxlbWVudElkZW50aWZpZXJsYWdlX2luX3llYXJz2BhYT6RmcmFuZG9tUBwuvU0MGGbT2h94xazpeqloZGlnZXN0SUQBbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTLYGFhdpGZyYW5kb21Qo6kOsHqedb_9xHVlfCXHf2hkaWdlc3RJRAJsZWxlbWVudFZhbHVlZTUxMTQ3cWVsZW1lbnRJZGVudGlmaWVydHJlc2lkZW50X3Bvc3RhbF9jb2Rl2BhYVaRmcmFuZG9tUP6aK3BnaJ4ssYCnhgPSaZpoZGlnZXN0SUQDbGVsZW1lbnRWYWx1ZWZCRVJMSU5xZWxlbWVudElkZW50aWZpZXJrYmlydGhfcGxhY2XYGFhPpGZyYW5kb21QGR_ZD_ylLFjp_gFyoXxR0WhkaWdlc3RJRARsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8xNNgYWFWkZnJhbmRvbVByTlMf_mCOUvaECM5veox_aGRpZ2VzdElEBWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJvaXNzdWluZ19jb3VudHJ52BhYY6RmcmFuZG9tUED3uH1EYolIFfAdQr8v6pVoZGlnZXN0SUQGbGVsZW1lbnRWYWx1ZcB0MTk2NC0wOC0xMlQwMDowMDowMFpxZWxlbWVudElkZW50aWZpZXJqYmlydGhfZGF0ZdgYWE-kZnJhbmRvbVBucDIRMDGt1bMXZVQopw3OaGRpZ2VzdElEB2xlbGVtZW50VmFsdWX0cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzY12BhYVqRmcmFuZG9tUEQqTillqXQcpIwC8F2YOMloZGlnZXN0SUQIbGVsZW1lbnRWYWx1ZWJERXFlbGVtZW50SWRlbnRpZmllcnByZXNpZGVudF9jb3VudHJ52BhYT6RmcmFuZG9tUMoKXZZ4ZDwVRRL4IQ7oDEFoZGlnZXN0SUQJbGVsZW1lbnRWYWx1ZfVxZWxlbWVudElkZW50aWZpZXJrYWdlX292ZXJfMTbYGFhXpGZyYW5kb21QdJ-5Oz_55VjO0LOBbnoLs2hkaWdlc3RJRApsZWxlbWVudFZhbHVlYkRFcWVsZW1lbnRJZGVudGlmaWVycWlzc3VpbmdfYXV0aG9yaXR52BhYa6RmcmFuZG9tUMexUIlyfvCgcIUu67OBH6doZGlnZXN0SUQLbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDItMThUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcm1pc3N1YW5jZV9kYXRl2BhYVKRmcmFuZG9tUJ_7jstnoovdbm84Cmh2etFoZGlnZXN0SUQMbGVsZW1lbnRWYWx1ZRkHrHFlbGVtZW50SWRlbnRpZmllcm5hZ2VfYmlydGhfeWVhctgYWFmkZnJhbmRvbVAnc4IFpUS4gxjqo-1DsQNvaGRpZ2VzdElEDWxlbGVtZW50VmFsdWVqTVVTVEVSTUFOTnFlbGVtZW50SWRlbnRpZmllcmtmYW1pbHlfbmFtZdgYWE-kZnJhbmRvbVD0dq9e6pNoaa0e_tVlZ-hZaGRpZ2VzdElEDmxlbGVtZW50VmFsdWX1cWVsZW1lbnRJZGVudGlmaWVya2FnZV9vdmVyXzE42BhYU6RmcmFuZG9tUIurbtyPoiia4qsc62iQHIBoZGlnZXN0SUQPbGVsZW1lbnRWYWx1ZWVFUklLQXFlbGVtZW50SWRlbnRpZmllcmpnaXZlbl9uYW1l2BhYY6RmcmFuZG9tUKgfL0gkbSOApy2APkdkNatoZGlnZXN0SUQQbGVsZW1lbnRWYWx1ZXBIRUlERVNUUkHhup5FIDE3cWVsZW1lbnRJZGVudGlmaWVyb3Jlc2lkZW50X3N0cmVldNgYWFGkZnJhbmRvbVA3gWJEwZz8jgsLsfRJvjMQaGRpZ2VzdElEEWxlbGVtZW50VmFsdWViREVxZWxlbWVudElkZW50aWZpZXJrbmF0aW9uYWxpdHnYGFhPpGZyYW5kb21QHSMBCaBxBPPy92dCcmoZvWhkaWdlc3RJRBJsZWxlbWVudFZhbHVl9XFlbGVtZW50SWRlbnRpZmllcmthZ2Vfb3Zlcl8yMdgYWFakZnJhbmRvbVB4Df01yH0SBmag1gS4xKL9aGRpZ2VzdElEE2xlbGVtZW50VmFsdWVlS8OWTE5xZWxlbWVudElkZW50aWZpZXJtcmVzaWRlbnRfY2l0edgYWFukZnJhbmRvbVDxOTqapogRuHVS1cLoK7z6aGRpZ2VzdElEFGxlbGVtZW50VmFsdWVmR0FCTEVScWVsZW1lbnRJZGVudGlmaWVycWZhbWlseV9uYW1lX2JpcnRo2BhYaaRmcmFuZG9tUOFMkL6pWaVejQQEv7_aS-loZGlnZXN0SUQVbGVsZW1lbnRWYWx1ZcB4GDIwMjUtMDMtMDRUMTQ6MTI6MDQuMzc1WnFlbGVtZW50SWRlbnRpZmllcmtleHBpcnlfZGF0ZQ`; -const sdJwtCredentialIssuedByWalletEnterprise = `eyJ0eXAiOiJ2YytzZC1qd3QiLCJ2Y3RtIjpbImV5SjJZM1FpT2lKMWNtNDZaWFV1WlhWeWIzQmhMbVZqTG1WMVpHazZjR2xrT2pFaUxDSnVZVzFsSWpvaVZtVnlhV1pwWVdKc1pTQkpSQ0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lWR2hwY3lCcGN5QmhJRlpsY21sbWFXRmliR1VnU1VRZ1pHOWpkVzFsYm5RZ2FYTnpkV1ZrSUdKNUlIUm9aU0IzWld4c0lHdHViM2R1SUZaSlJDQkpjM04xWlhJaUxDSmthWE53YkdGNUlqcGJleUpzWVc1bklqb2laVzR0VlZNaUxDSnVZVzFsSWpvaVZtVnlhV1pwWVdKc1pTQkpSQ0lzSW5KbGJtUmxjbWx1WnlJNmV5SnphVzF3YkdVaU9uc2liRzluYnlJNmV5SjFjbWtpT2lKb2RIUndPaTh2ZDJGc2JHVjBMV1Z1ZEdWeWNISnBjMlV0YVhOemRXVnlPamd3TURNdmFXMWhaMlZ6TDJ4dloyOHVjRzVuSWl3aWRYSnBJMmx1ZEdWbmNtbDBlU0k2SW5Ob1lUSTFOaTFoWTJSaE16UXdOR015WTJZME5tUmhNVGt5WTJZeU5EVmpZMk0yWWpreFpXUmpaVGc0TmpreE1qSm1ZVFZoTmpZek5qSTROR1l4WVRZd1ptWmpaRGcySWl3aVlXeDBYM1JsZUhRaU9pSldTVVFnVEc5bmJ5SjlMQ0ppWVdOclozSnZkVzVrWDJOdmJHOXlJam9pSXpSall6TmtaQ0lzSW5SbGVIUmZZMjlzYjNJaU9pSWpSa1pHUmtaR0luMHNJbk4yWjE5MFpXMXdiR0YwWlhNaU9sdDdJblZ5YVNJNkltaDBkSEE2THk5M1lXeHNaWFF0Wlc1MFpYSndjbWx6WlMxcGMzTjFaWEk2T0RBd015OXBiV0ZuWlhNdmRHVnRjR3hoZEdVdGNHbGtMbk4yWnlKOVhYMTlYU3dpWTJ4aGFXMXpJanBiZXlKd1lYUm9JanBiSW1kcGRtVnVYMjVoYldVaVhTd2laR2x6Y0d4aGVTSTZXM3NpYkdGdVp5STZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkhhWFpsYmlCT1lXMWxJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlVhR1VnWjJsMlpXNGdibUZ0WlNCdlppQjBhR1VnVmtsRUlHaHZiR1JsY2lKOVhTd2ljM1puWDJsa0lqb2laMmwyWlc1ZmJtRnRaU0o5TEhzaWNHRjBhQ0k2V3lKbVlXMXBiSGxmYm1GdFpTSmRMQ0prYVhOd2JHRjVJanBiZXlKc1lXNW5Jam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJa1poYldsc2VTQk9ZVzFsSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpVYUdVZ1ptRnRhV3g1SUc1aGJXVWdiMllnZEdobElGWkpSQ0JvYjJ4a1pYSWlmVjBzSW5OMloxOXBaQ0k2SW1aaGJXbHNlVjl1WVcxbEluMHNleUp3WVhSb0lqcGJJbUpwY25Sb1gyUmhkR1VpWFN3aVpHbHpjR3hoZVNJNlczc2liR0Z1WnlJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKQ2FYSjBhQ0JrWVhSbElpd2laR1Z6WTNKcGNIUnBiMjRpT2lKVWFHVWdZbWx5ZEdnZ1pHRjBaU0J2WmlCMGFHVWdWa2xFSUdodmJHUmxjaUo5WFN3aWMzWm5YMmxrSWpvaVltbHlkR2hmWkdGMFpTSjlMSHNpY0dGMGFDSTZXeUpwYzNOMWFXNW5YMkYxZEdodmNtbDBlU0pkTENKa2FYTndiR0Y1SWpwYmV5SnNZVzVuSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtsemMzVnBibWNnWVhWMGFHOXlhWFI1SWl3aVpHVnpZM0pwY0hScGIyNGlPaUpVYUdVZ2FYTnpkV2x1WnlCaGRYUm9iM0pwZEhrZ2IyWWdkR2hsSUZaSlJDQmpjbVZrWlc1MGFXRnNJbjFkTENKemRtZGZhV1FpT2lKcGMzTjFhVzVuWDJGMWRHaHZjbWwwZVNKOUxIc2ljR0YwYUNJNld5SnBjM04xWVc1alpWOWtZWFJsSWwwc0ltUnBjM0JzWVhraU9sdDdJbXhoYm1jaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVNYTnpkV0Z1WTJVZ1pHRjBaU0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lWR2hsSUdSaGRHVWdkR2hoZENCMGFHVWdZM0psWkdWdWRHbGhiQ0IzWVhNZ2FYTnpkV1ZrSW4xZExDSnpkbWRmYVdRaU9pSnBjM04xWVc1alpWOWtZWFJsSW4wc2V5SndZWFJvSWpwYkltVjRjR2x5ZVY5a1lYUmxJbDBzSW1ScGMzQnNZWGtpT2x0N0lteGhibWNpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pU1hOemRXRnVZMlVnWkdGMFpTSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVZHaGxJR1JoZEdVZ2RHaGhkQ0IwYUdVZ1kzSmxaR1Z1ZEdsaGJDQjNhV3hzSUdWNGNHbHlaU0o5WFN3aWMzWm5YMmxrSWpvaVpYaHdhWEo1WDJSaGRHVWlmVjE5Il0sIng1YyI6WyJNSUlDUkRDQ0FldWdBd0lCQWdJVUVRZzNOVzdBVHJFeFpWUWRSZjdPeXpZYnAzMHdDZ1lJS29aSXpqMEVBd0l3VWpFTE1Ba0dBMVVFQmhNQ1IxSXhEekFOQmdOVkJBZ01Ca2R5WldWalpURVBNQTBHQTFVRUJ3d0dRWFJvWlc1ek1RNHdEQVlEVlFRS0RBVkhWVzVsZERFUk1BOEdBMVVFQXd3SWQzZFhZV3hzWlhRd0hoY05NalV3TWpJMk1UVXhOak16V2hjTk16VXdNakkwTVRVeE5qTXpXakIxTVFzd0NRWURWUVFHRXdKSFVqRVBNQTBHQTFVRUNBd0dSM0psWldObE1ROHdEUVlEVlFRSERBWkJkR2hsYm5NeERqQU1CZ05WQkFvTUJVZFZibVYwTVJFd0R3WURWUVFMREFoSlpHVnVkR2wwZVRFaE1COEdBMVVFQXd3WVYyRnNiR1YwSUVWdWRHVnljSEpwYzJVZ1NYTnpkV1Z5TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFU1YvSXlnUnJVVjJNNEIxTUEra0JFYVoyTllUV1Q1bHdKbWF5Y09KNEJyYkJoY0w0eDVncE1GVHdZdUprK043cGVWZ25mendOWDZEeU9mK3lkNTJvbmFOOE1Ib3dDUVlEVlIwVEJBSXdBREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUIwR0ExVWREZ1FXQkJUdXJUbi8yK1hVNkdic3UvZ1RsZDRadU9CaGpUQWZCZ05WSFNNRUdEQVdnQlI4ZVBodlBLK2ppNmNmS3dhMzZrMXJSZXdGb2pBS0JnZ3Foa2pPUFFRREFnTkhBREJFQWlBcURoL1l0S1RPa29YaEZmRVFQMU0rOUxOQi9IcTliWGo4TWt6MHZhWElQd0lnYW82dTE2NlhjdDB1YS9NcXUzUHlvRis2aHlWUVVGb1FWY3dRdWsyNC95QT0iXSwiYWxnIjoiRVMyNTYifQ.eyJjbmYiOnsiandrIjp7ImNydiI6IlAtMjU2IiwiZXh0Ijp0cnVlLCJrZXlfb3BzIjpbInZlcmlmeSJdLCJrdHkiOiJFQyIsIngiOiJjalZ6T3RUUXEwbTNqS3pfS251Ql9EUERlQ0FjbTlRcm9rM3JVTTgzUjRjIiwieSI6ImpON2dhd1dOOVFFZ1l5cENQQ19KelV6YXFxYWRyRmpoOGJXa213NUNMYjgifX0sInZjdCI6InVybjpldS5ldXJvcGEuZWMuZXVkaTpwaWQ6MSIsImp0aSI6InVybjp2aWQ6OTU2MTFhMWUtNzNjZi00ZmE3LThhMjctZjE0YzgyNTFhNTRlIiwiaWF0IjoxNzQxMTA2OTc1LCJleHAiOjE3NzI2NDI5NzUsImlzcyI6Imh0dHA6Ly93YWxsZXQtZW50ZXJwcmlzZS1pc3N1ZXI6ODAwMyIsInN1YiI6Ilhxcko1My13anNCWjNBUmlzQnJ1dmRwRk9qdnRSWGxMZzNmUWJuZmJfbVUiLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyItalZ2ZTM4RnNxUkRLU0hFNDlfSGJadHJYZ3VBQTFWV0dKaU00dGZCU0xZIiwiMEZ5Y1hhNzVNcTNyUHBqR1FLM0lrV3N6d0tZYmVCSnAwYndIYWl2MkcyTSIsIjRGSTAwV1JnOFFCR2ZoemtLYmdGNTM3N29DSHZ3TUlKSmFOTTVLUmNMTzgiLCI3VjN4LXhtS2MzemNBd0JVcm0ycDAzZGVkX1U2RUVmM3dqaVROd1dLeE9jIiwiOEg4M0FNQjh0NUtxVmRkcmNIbldPN0s4aUVYbXNBU0w3ZlpPd2Vnc04wMCIsIkVRMGVfcE9sOUJ0emRneWJoVnlScWxvMDdzbmo4N0Vab3h5NVIzcXktbW8iLCJGNm95UmdhSDBDalRxcVFIRjBKUVNVVGhpdnZPZV8xWEhicDdmbXFSWHlzIiwiSEMyRXR2cGl3MktVd0NSOE12N3ZpZU9obzV2Vk5WQVJrdVFzeW9kR3lTZyIsIk90MDAtTjhMNG1sdTF5cXJNTHhfTmZCT29RT1FSaWdYQWhnX0Q4STlFYnMiLCJfdm5NYU5WZ0JrTUpybmd0UEZwVkpsZ0h5djFPdUhiZWRSU2pWVWk1SVRzIiwiZGUzbWhlMHZCV0NVWkxfdThrNi1CN2hlb0NqOHYwaG9BUjFISUl5ajBUayIsImZlWnd1bkdERG8xdi13dE1XNUpwX21TUnNfbmM1MC01UGYwUmlrTXhpaFEiLCJtV1BzRF96UWFLclBNS1NkdEhocTNmR0xKaVJOenlWdEFWVjZ3U2RUZEtVIiwicTR5UWZJMl9NWVVKcGl6VWF3bUNCczlYYnBNVk43NjNueFZ5aHBQR1M1TSIsInJGUW9QSVJ5OFM1R3BkWjR5OTRSTV9NUUpDQ3Mzamh4anE1SDZNYzFIU2MiLCJyYzd4a1hEcVk3c3VBYlg3MmpLMC12ZEpNRi1tem5ZZXpkR1ktMzBiblZBIiwidER4NU1vODNtWXpNbElVZlpVaDgxdWZORTNydzZRa0dCNjRidEdqRmtBYyJdfQ.-B1l8lRrWnvVFwtMW4AC02wNR6DxHmAwPkbHeLAGd_GFqbx7SiKPgNUNw-xr1A8U73yIxC4iAWXt3CLFn4GKHg~WyJGNVhBVE1LNlBjY1ZNeXcwcUs5VmhnIiwiZmFtaWx5X25hbWUiLCJEb2UiXQ~WyJWMWxFek91bWxIclc0R1Z6M3FTWDh3IiwiZmFtaWx5X25hbWVfYmlydGgiLCJEb2UiXQ~WyJNX1ViMUZWYnphTjhVUEV1QlZ2VkN3IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJ5azZBaGhZVDJHdDEwUUY5czFEQ1hRIiwiZ2l2ZW5fbmFtZV9iaXJ0aCIsIkpvaG4iXQ~WyJEcEpiWlZuSWVXY3JEekJCNlUtaDVnIiwiYmlydGhfZGF0ZSIsIjE5OTAtMTAtMTUiXQ~WyJNd016YXRvNndLb3NWN2lqazVQMjBRIiwiaXNzdWluZ19hdXRob3JpdHkiLCJQSUQ6MDAwMDEiXQ~WyI4a2N5Y3EtQ1g4NWhxRUhyU3h2ZjN3IiwiaXNzdWluZ19jb3VudHJ5IiwiR1IiXQ~WyIxOXI2TGdEVnpmZGRzSDJPODRfWjNRIiwiZG9jdW1lbnRfbnVtYmVyIiwiMTIzMTMyMTMiXQ~WyI1OE51c1lITGd3T2FlZlp3UjEtN0JBIiwiaXNzdWFuY2VfZGF0ZSIsIjIwMjUtMDMtMDQiXQ~WyJ3Qy1remJxM1E2LUZJLXRWTnZ3aFFBIiwiZXhwaXJ5X2RhdGUiLCIyMDM1LTA0LTIxIl0~WyJGREg4ck1hdGVhdVg0Y1N0eHRDYjNRIiwiYWdlX292ZXJfMTgiLGZhbHNlXQ~WyJZaUFFWVNQTy1ndE1rcVRqRmc1NmlnIiwic2V4IiwxXQ~WyJrS0Y3dF92VU5qNTJ1M2hGLWgwSTlnIiwibmF0aW9uYWxpdHkiLCJVUyJd~WyJERnYyVUdaR3hmbUFXN25xRURQS0NBIiwiYmlydGhfcGxhY2UiLCJVUyJd~WyJDWDBBNFRzZDk5djF2d2cyeEtra1VRIiwicmVzaWRlbnRfYWRkcmVzcyIsIjIzLCBSYW5kb20gc3RyLiAzNDc5MyBBcHQgMyBVU0EiXQ~WyIyNzRCcHI4dmdtRFM1SUxxR0pWMXl3IiwiZW1haWxfYWRkcmVzcyIsImpvaG5Ac2FtcGxlLmNvbSJd~WyIwZHBsWEE3NDNDVm9TZmZxS1ZyclRRIiwibW9iaWxlX3Bob25lX251bWJlciIsIiszMDgzODgzMzgzODIiXQ~`; +const exampleCredential = `eyJ0eXAiOiJ2YytzZC1qd3QiLCJ2Y3RtIjpbImV5SjJZM1FpT2lKMWNtNDZaWFV1WlhWeWIzQmhMbVZqTG1WMVpHazZjR2xrT2pFaUxDSnVZVzFsSWpvaVZtVnlhV1pwWVdKc1pTQkpSQ0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lWR2hwY3lCcGN5QmhJRlpsY21sbWFXRmliR1VnU1VRZ1pHOWpkVzFsYm5RZ2FYTnpkV1ZrSUdKNUlIUm9aU0IzWld4c0lHdHViM2R1SUZaSlJDQkpjM04xWlhJaUxDSmthWE53YkdGNUlqcGJleUpzWVc1bklqb2laVzR0VlZNaUxDSnVZVzFsSWpvaVZtVnlhV1pwWVdKc1pTQkpSQ0lzSW5KbGJtUmxjbWx1WnlJNmV5SnphVzF3YkdVaU9uc2liRzluYnlJNmV5SjFjbWtpT2lKb2RIUndPaTh2ZDJGc2JHVjBMV1Z1ZEdWeWNISnBjMlV0YVhOemRXVnlPamd3TURNdmFXMWhaMlZ6TDJ4dloyOHVjRzVuSWl3aWRYSnBJMmx1ZEdWbmNtbDBlU0k2SW5Ob1lUSTFOaTFoWTJSaE16UXdOR015WTJZME5tUmhNVGt5WTJZeU5EVmpZMk0yWWpreFpXUmpaVGc0TmpreE1qSm1ZVFZoTmpZek5qSTROR1l4WVRZd1ptWmpaRGcySWl3aVlXeDBYM1JsZUhRaU9pSldTVVFnVEc5bmJ5SjlMQ0ppWVdOclozSnZkVzVrWDJOdmJHOXlJam9pSXpSall6TmtaQ0lzSW5SbGVIUmZZMjlzYjNJaU9pSWpSa1pHUmtaR0luMHNJbk4yWjE5MFpXMXdiR0YwWlhNaU9sdDdJblZ5YVNJNkltaDBkSEE2THk5M1lXeHNaWFF0Wlc1MFpYSndjbWx6WlMxcGMzTjFaWEk2T0RBd015OXBiV0ZuWlhNdmRHVnRjR3hoZEdVdGNHbGtMbk4yWnlKOVhYMTlYU3dpWTJ4aGFXMXpJanBiZXlKd1lYUm9JanBiSW1kcGRtVnVYMjVoYldVaVhTd2laR2x6Y0d4aGVTSTZXM3NpYkdGdVp5STZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkhhWFpsYmlCT1lXMWxJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlVhR1VnWjJsMlpXNGdibUZ0WlNCdlppQjBhR1VnVmtsRUlHaHZiR1JsY2lKOVhTd2ljM1puWDJsa0lqb2laMmwyWlc1ZmJtRnRaU0o5TEhzaWNHRjBhQ0k2V3lKbVlXMXBiSGxmYm1GdFpTSmRMQ0prYVhOd2JHRjVJanBiZXlKc1lXNW5Jam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJa1poYldsc2VTQk9ZVzFsSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpVYUdVZ1ptRnRhV3g1SUc1aGJXVWdiMllnZEdobElGWkpSQ0JvYjJ4a1pYSWlmVjBzSW5OMloxOXBaQ0k2SW1aaGJXbHNlVjl1WVcxbEluMHNleUp3WVhSb0lqcGJJbUpwY25Sb1gyUmhkR1VpWFN3aVpHbHpjR3hoZVNJNlczc2liR0Z1WnlJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKQ2FYSjBhQ0JrWVhSbElpd2laR1Z6WTNKcGNIUnBiMjRpT2lKVWFHVWdZbWx5ZEdnZ1pHRjBaU0J2WmlCMGFHVWdWa2xFSUdodmJHUmxjaUo5WFN3aWMzWm5YMmxrSWpvaVltbHlkR2hmWkdGMFpTSjlMSHNpY0dGMGFDSTZXeUpwYzNOMWFXNW5YMkYxZEdodmNtbDBlU0pkTENKa2FYTndiR0Y1SWpwYmV5SnNZVzVuSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtsemMzVnBibWNnWVhWMGFHOXlhWFI1SWl3aVpHVnpZM0pwY0hScGIyNGlPaUpVYUdVZ2FYTnpkV2x1WnlCaGRYUm9iM0pwZEhrZ2IyWWdkR2hsSUZaSlJDQmpjbVZrWlc1MGFXRnNJbjFkTENKemRtZGZhV1FpT2lKcGMzTjFhVzVuWDJGMWRHaHZjbWwwZVNKOUxIc2ljR0YwYUNJNld5SnBjM04xWVc1alpWOWtZWFJsSWwwc0ltUnBjM0JzWVhraU9sdDdJbXhoYm1jaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVNYTnpkV0Z1WTJVZ1pHRjBaU0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lWR2hsSUdSaGRHVWdkR2hoZENCMGFHVWdZM0psWkdWdWRHbGhiQ0IzWVhNZ2FYTnpkV1ZrSW4xZExDSnpkbWRmYVdRaU9pSnBjM04xWVc1alpWOWtZWFJsSW4wc2V5SndZWFJvSWpwYkltVjRjR2x5ZVY5a1lYUmxJbDBzSW1ScGMzQnNZWGtpT2x0N0lteGhibWNpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pU1hOemRXRnVZMlVnWkdGMFpTSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVZHaGxJR1JoZEdVZ2RHaGhkQ0IwYUdVZ1kzSmxaR1Z1ZEdsaGJDQjNhV3hzSUdWNGNHbHlaU0o5WFN3aWMzWm5YMmxrSWpvaVpYaHdhWEo1WDJSaGRHVWlmVjE5Il0sIng1YyI6W1siTUlJQjZEQ0NBWTJnQXdJQkFnSVVaTHNQMU1KeXA0WDUvQ1F6ekwrYXQ0bFJnRjB3Q2dZSUtvWkl6ajBFQXdJd1xuU1RFTk1Bc0dBMVVFQXd3RWRHVnpkREVMTUFrR0ExVUVCaE1DUmxJeERUQUxCZ05WQkFnTUJIUmxjM1F4RFRBTFxuQmdOVkJBY01CSFJsYzNReERUQUxCZ05WQkFvTUJIUmxjM1F3SGhjTk1qVXdOVEk0TURreE1UQXdXaGNOTXpVd1xuTlRJMk1Ea3hNVEF3V2pCSk1RMHdDd1lEVlFRRERBUjBaWE4wTVFzd0NRWURWUVFHRXdKR1VqRU5NQXNHQTFVRVxuQ0F3RWRHVnpkREVOTUFzR0ExVUVCd3dFZEdWemRERU5NQXNHQTFVRUNnd0VkR1Z6ZERCWk1CTUdCeXFHU000OVxuQWdFR0NDcUdTTTQ5QXdFSEEwSUFCQnE2bXExTThJZ25aNkYwTTY2dXNyZjYzV09ROUpwRTFFK0gxTFIvNy8wQlxuWW9uaWVBMjhOOFdYOE52ZTMwK0MzU3pYWjR6TVJtVFBlT1lmMzZCZTRNaWpVekJSTUIwR0ExVWREZ1FXQkJTT1xuSEJYdDlTU2lNRDBOZjF0cHhtcnI0MEo1VWpBZkJnTlZIU01FR0RBV2dCU09IQlh0OVNTaU1EME5mMXRweG1yclxuNDBKNVVqQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01Bb0dDQ3FHU000OUJBTUNBMGtBTUVZQ0lRRHNhc2ttWUdDTlxud2hpWW02TXRXYUN3QXVxdDhnUnJlM3FDWjBpTHFudUIwd0loQVBNa2VFaHVNLzBoSU0vSkpXK2NnNExPWXB3ZlxuK2Z5Wmw4VHRRWGtZTTNuNCJdXSwiYWxnIjoiRVMyNTYifQ.eyJjbmYiOnsiandrIjp7Imt0eSI6IkVDIiwieCI6IkdycWFyVXp3aUNkbm9YUXpycTZ5dF9yZFk1RDBta1RVVDRmVXRIX3ZfUUUiLCJ5IjoiWW9uaWVBMjhOOFdYOE52ZTMwLUMzU3pYWjR6TVJtVFBlT1lmMzZCZTRNZyIsImNydiI6IlAtMjU2In19LCJ2Y3QiOiJ1cm46ZXUuZXVyb3BhLmVjLmV1ZGk6cGlkOjEiLCJqdGkiOiJ1cm46dmlkOjk1NjExYTFlLTczY2YtNGZhNy04YTI3LWYxNGM4MjUxYTU0ZSIsImlhdCI6MTc0MTEwNjk3NSwiZXhwIjoxNzcyNjQyOTc1LCJpc3MiOiJodHRwOi8vd2FsbGV0LWVudGVycHJpc2UtaXNzdWVyOjgwMDMiLCJzdWIiOiJYcXJKNTMtd2pzQlozQVJpc0JydXZkcEZPanZ0UlhsTGczZlFibmZiX21VIiwiX3NkX2FsZyI6InNoYS0yNTYiLCJfc2QiOlsiQjhxTnYxWExxa3JnRFRsSkM2VTZDRTZ3VjFfVExocUh0WXhVUW1FaVVFNCIsIjM3RUpKRzRaRXhOeHM3d20zN2RhRGJOd0Q1M2w0Qlg3ckVKV0hHTUVaR1UiLCJkQUdKb3dSWGdrNlFReG9IR3A4MTdSLUJQUTR0UWprQXlTMG5nd3ZBbEV3IiwiY0MzQ1BJcGVwcEU0TURMLUYya3lXUUpsVlhfbDhzOVZWeE5kbkVHUThWZyIsInhzZGhIRGFQVFBrbTY1cmkxZWhuTnVfbDV3Q24zQk42V2hlcDh0N0N0R1kiLCIzWXVnVVQ5X3B5SDZJZmJveHZEeFlVWkVRN0tFNngybDJIOFNOUEUzdjB3IiwiRjBESzZGTjhiRVZWV05zOTVMbXBSSlZmM2x0TVBXTFZsb09kdkY3UzB6cyIsIk8tUXdJcUVPSERuSFVLNmRrczhCdDcyUUJnV28tWFJJZjdrVHRiQWg5Q28iLCJMNlNBZVN1bXVMbzZHeVBwRkZ6VFh5dTFPZ0dLM0Y0clNkUUVISlhZYzNJIiwiU3pFVFg5cERjUDhlX1FxbEd4QV93ZFY4cXVia2lyNUIwMno3eTdNcEZQTSIsInpxd1ZPeHRpcE0yeVNmbVVXOVhUMFNRNFJ1UHprMFRNejdidE90dUVLcFkiLCJKQW1VUmZWb015eTZDa19iVElqbTZYcUFWeGVFZl9PZGgxQnFrQ3FJX21rIiwiTk9WN3JYcWxLNk5fTHRRXzEzMkF2SXl1cW1FdXo4dGZVNWRULXBvODc3OCIsIm93Z0FaQUd1NXJHNWFCYUtBYkc2MTc4NFhaLW5YOE1SaEhqams0Z1BUWjAiLCJSbjFJZEZmcnV6WXlYQmF2WHlob0ZFWk1FR3hCazZiWGZwTWRlODZVTEU4Iiwid01PSm1NMUF2OFMyMWlDWEJyczB1aFo0UkRIM21WS2RjdUhXX0xwYTFLRSJdfQ.fxtqsiciwyYENEzWR_HK4xXRT1T2Bvt0tYk_vl5ovCthuFI3jA6ig1cIG8hTOEHx9SQRcQ6dLOmOBbYCN0LUpw~WyIxLm1yenI5NGlwZ3giLCJleHBpcnlfZGF0ZSIsIjIwMzUtMDQtMjEiXQ~WyIxLjQwYzA3MGZpdWUiLCJiaXJ0aF9wbGFjZSIsIlVTIl0~WyIxLmtkaW1uMDM0Z2ciLCJiaXJ0aF9kYXRlIiwiMTk5MC0xMC0xNSJd~WyIxLmNpMmoxbzh3dW0iLCJpc3N1YW5jZV9kYXRlIiwiMjAyNS0wMy0wNCJd~WyIxLmo4Mm9oNDVibSIsImRvY3VtZW50X251bWJlciIsIjEyMzEzMjEzIl0~WyIxLmZmc3pkdHgwcHciLCJuYXRpb25hbGl0eSIsWyJVUyJdXQ~WyIxLmc4Z3ExbjVxaXpmIiwiZW1haWxfYWRkcmVzcyIsImpvaG5Ac2FtcGxlLmNvbSJd~WyIxLjA0MDZ2bWgxbSIsImFnZV9vdmVyXzE4IixmYWxzZV0~WyIxLmxvcGhrY3dzbHciLCJtb2JpbGVfcGhvbmVfbnVtYmVyIiwiKzMwODM4ODMzODM4MiJd~WyIxLjE5ZW5hYmUyeGYiLCJyZXNpZGVudF9hZGRyZXNzIiwiMjMsIFJhbmRvbSBzdHIuIDM0NzkzIEFwdCAzIFVTQSJd~WyIxLnd2aGpvaHprdXEiLCJnaXZlbl9uYW1lX2JpcnRoIiwiSm9obiJd~WyIxLmNsN2lmcHc2bG4iLCJnaXZlbl9uYW1lIiwiSm9obiJd~WyIxLjI2Zjh6ZWh5ZDEiLCJmYW1pbHlfbmFtZSIsIkRvZSJd~WyIxLmhiOWh2bTdiY2wiLCJpc3N1aW5nX2F1dGhvcml0eSIsIlBJRDowMDAwMSJd~WyIxLnhrNWV3cGx2b3EiLCJpc3N1aW5nX2NvdW50cnkiLCJHUiJd~WyIxLmw1cnhiOGh2cWgiLCJzZXgiLDFd~`; + +const exampleCert = `-----BEGIN CERTIFICATE----- +MIIB6DCCAY2gAwIBAgIUZLsP1MJyp4X5/CQzzL+at4lRgF0wCgYIKoZIzj0EAwIw +STENMAsGA1UEAwwEdGVzdDELMAkGA1UEBhMCRlIxDTALBgNVBAgMBHRlc3QxDTAL +BgNVBAcMBHRlc3QxDTALBgNVBAoMBHRlc3QwHhcNMjUwNTI4MDkxMTAwWhcNMzUw +NTI2MDkxMTAwWjBJMQ0wCwYDVQQDDAR0ZXN0MQswCQYDVQQGEwJGUjENMAsGA1UE +CAwEdGVzdDENMAsGA1UEBwwEdGVzdDENMAsGA1UECgwEdGVzdDBZMBMGByqGSM49 +AgEGCCqGSM49AwEHA0IABBq6mq1M8IgnZ6F0M66usrf63WOQ9JpE1E+H1LR/7/0B +YonieA28N8WX8Nve30+C3SzXZ4zMRmTPeOYf36Be4MijUzBRMB0GA1UdDgQWBBSO +HBXt9SSiMD0Nf1tpxmrr40J5UjAfBgNVHSMEGDAWgBSOHBXt9SSiMD0Nf1tpxmrr +40J5UjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQDsaskmYGCN +whiYm6MtWaCwAuqt8gRre3qCZ0iLqnuB0wIhAPMkeEhuM/0hIM/JJW+cg4LOYpwf ++fyZl8TtQXkYM3n4 +-----END CERTIFICATE-----`; -const sdJwtCredentialIssuedByWalletEnterpriseWithKbJwt = `eyJ0eXAiOiJ2YytzZC1qd3QiLCJ2Y3RtIjpbImV5SjJZM1FpT2lKMWNtNDZaWFV1WlhWeWIzQmhMbVZqTG1WMVpHazZjR2xrT2pFaUxDSnVZVzFsSWpvaVZtVnlhV1pwWVdKc1pTQkpSQ0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lWR2hwY3lCcGN5QmhJRlpsY21sbWFXRmliR1VnU1VRZ1pHOWpkVzFsYm5RZ2FYTnpkV1ZrSUdKNUlIUm9aU0IzWld4c0lHdHViM2R1SUZaSlJDQkpjM04xWlhJaUxDSmthWE53YkdGNUlqcGJleUpzWVc1bklqb2laVzR0VlZNaUxDSnVZVzFsSWpvaVZtVnlhV1pwWVdKc1pTQkpSQ0lzSW5KbGJtUmxjbWx1WnlJNmV5SnphVzF3YkdVaU9uc2liRzluYnlJNmV5SjFjbWtpT2lKb2RIUndPaTh2ZDJGc2JHVjBMV1Z1ZEdWeWNISnBjMlV0YVhOemRXVnlPamd3TURNdmFXMWhaMlZ6TDJ4dloyOHVjRzVuSWl3aWRYSnBJMmx1ZEdWbmNtbDBlU0k2SW5Ob1lUSTFOaTFoWTJSaE16UXdOR015WTJZME5tUmhNVGt5WTJZeU5EVmpZMk0yWWpreFpXUmpaVGc0TmpreE1qSm1ZVFZoTmpZek5qSTROR1l4WVRZd1ptWmpaRGcySWl3aVlXeDBYM1JsZUhRaU9pSldTVVFnVEc5bmJ5SjlMQ0ppWVdOclozSnZkVzVrWDJOdmJHOXlJam9pSXpSall6TmtaQ0lzSW5SbGVIUmZZMjlzYjNJaU9pSWpSa1pHUmtaR0luMHNJbk4yWjE5MFpXMXdiR0YwWlhNaU9sdDdJblZ5YVNJNkltaDBkSEE2THk5M1lXeHNaWFF0Wlc1MFpYSndjbWx6WlMxcGMzTjFaWEk2T0RBd015OXBiV0ZuWlhNdmRHVnRjR3hoZEdVdGNHbGtMbk4yWnlKOVhYMTlYU3dpWTJ4aGFXMXpJanBiZXlKd1lYUm9JanBiSW1kcGRtVnVYMjVoYldVaVhTd2laR2x6Y0d4aGVTSTZXM3NpYkdGdVp5STZJbVZ1TFZWVElpd2liR0ZpWld3aU9pSkhhWFpsYmlCT1lXMWxJaXdpWkdWelkzSnBjSFJwYjI0aU9pSlVhR1VnWjJsMlpXNGdibUZ0WlNCdlppQjBhR1VnVmtsRUlHaHZiR1JsY2lKOVhTd2ljM1puWDJsa0lqb2laMmwyWlc1ZmJtRnRaU0o5TEhzaWNHRjBhQ0k2V3lKbVlXMXBiSGxmYm1GdFpTSmRMQ0prYVhOd2JHRjVJanBiZXlKc1lXNW5Jam9pWlc0dFZWTWlMQ0pzWVdKbGJDSTZJa1poYldsc2VTQk9ZVzFsSWl3aVpHVnpZM0pwY0hScGIyNGlPaUpVYUdVZ1ptRnRhV3g1SUc1aGJXVWdiMllnZEdobElGWkpSQ0JvYjJ4a1pYSWlmVjBzSW5OMloxOXBaQ0k2SW1aaGJXbHNlVjl1WVcxbEluMHNleUp3WVhSb0lqcGJJbUpwY25Sb1gyUmhkR1VpWFN3aVpHbHpjR3hoZVNJNlczc2liR0Z1WnlJNkltVnVMVlZUSWl3aWJHRmlaV3dpT2lKQ2FYSjBhQ0JrWVhSbElpd2laR1Z6WTNKcGNIUnBiMjRpT2lKVWFHVWdZbWx5ZEdnZ1pHRjBaU0J2WmlCMGFHVWdWa2xFSUdodmJHUmxjaUo5WFN3aWMzWm5YMmxrSWpvaVltbHlkR2hmWkdGMFpTSjlMSHNpY0dGMGFDSTZXeUpwYzNOMWFXNW5YMkYxZEdodmNtbDBlU0pkTENKa2FYTndiR0Y1SWpwYmV5SnNZVzVuSWpvaVpXNHRWVk1pTENKc1lXSmxiQ0k2SWtsemMzVnBibWNnWVhWMGFHOXlhWFI1SWl3aVpHVnpZM0pwY0hScGIyNGlPaUpVYUdVZ2FYTnpkV2x1WnlCaGRYUm9iM0pwZEhrZ2IyWWdkR2hsSUZaSlJDQmpjbVZrWlc1MGFXRnNJbjFkTENKemRtZGZhV1FpT2lKcGMzTjFhVzVuWDJGMWRHaHZjbWwwZVNKOUxIc2ljR0YwYUNJNld5SnBjM04xWVc1alpWOWtZWFJsSWwwc0ltUnBjM0JzWVhraU9sdDdJbXhoYm1jaU9pSmxiaTFWVXlJc0lteGhZbVZzSWpvaVNYTnpkV0Z1WTJVZ1pHRjBaU0lzSW1SbGMyTnlhWEIwYVc5dUlqb2lWR2hsSUdSaGRHVWdkR2hoZENCMGFHVWdZM0psWkdWdWRHbGhiQ0IzWVhNZ2FYTnpkV1ZrSW4xZExDSnpkbWRmYVdRaU9pSnBjM04xWVc1alpWOWtZWFJsSW4wc2V5SndZWFJvSWpwYkltVjRjR2x5ZVY5a1lYUmxJbDBzSW1ScGMzQnNZWGtpT2x0N0lteGhibWNpT2lKbGJpMVZVeUlzSW14aFltVnNJam9pU1hOemRXRnVZMlVnWkdGMFpTSXNJbVJsYzJOeWFYQjBhVzl1SWpvaVZHaGxJR1JoZEdVZ2RHaGhkQ0IwYUdVZ1kzSmxaR1Z1ZEdsaGJDQjNhV3hzSUdWNGNHbHlaU0o5WFN3aWMzWm5YMmxrSWpvaVpYaHdhWEo1WDJSaGRHVWlmVjE5Il0sIng1YyI6WyJNSUlDUkRDQ0FldWdBd0lCQWdJVUVRZzNOVzdBVHJFeFpWUWRSZjdPeXpZYnAzMHdDZ1lJS29aSXpqMEVBd0l3VWpFTE1Ba0dBMVVFQmhNQ1IxSXhEekFOQmdOVkJBZ01Ca2R5WldWalpURVBNQTBHQTFVRUJ3d0dRWFJvWlc1ek1RNHdEQVlEVlFRS0RBVkhWVzVsZERFUk1BOEdBMVVFQXd3SWQzZFhZV3hzWlhRd0hoY05NalV3TWpJMk1UVXhOak16V2hjTk16VXdNakkwTVRVeE5qTXpXakIxTVFzd0NRWURWUVFHRXdKSFVqRVBNQTBHQTFVRUNBd0dSM0psWldObE1ROHdEUVlEVlFRSERBWkJkR2hsYm5NeERqQU1CZ05WQkFvTUJVZFZibVYwTVJFd0R3WURWUVFMREFoSlpHVnVkR2wwZVRFaE1COEdBMVVFQXd3WVYyRnNiR1YwSUVWdWRHVnljSEpwYzJVZ1NYTnpkV1Z5TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFU1YvSXlnUnJVVjJNNEIxTUEra0JFYVoyTllUV1Q1bHdKbWF5Y09KNEJyYkJoY0w0eDVncE1GVHdZdUprK043cGVWZ25mendOWDZEeU9mK3lkNTJvbmFOOE1Ib3dDUVlEVlIwVEJBSXdBREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdIUVlEVlIwbEJCWXdGQVlJS3dZQkJRVUhBd0VHQ0NzR0FRVUZCd01DTUIwR0ExVWREZ1FXQkJUdXJUbi8yK1hVNkdic3UvZ1RsZDRadU9CaGpUQWZCZ05WSFNNRUdEQVdnQlI4ZVBodlBLK2ppNmNmS3dhMzZrMXJSZXdGb2pBS0JnZ3Foa2pPUFFRREFnTkhBREJFQWlBcURoL1l0S1RPa29YaEZmRVFQMU0rOUxOQi9IcTliWGo4TWt6MHZhWElQd0lnYW82dTE2NlhjdDB1YS9NcXUzUHlvRis2aHlWUVVGb1FWY3dRdWsyNC95QT0iXSwiYWxnIjoiRVMyNTYifQ.eyJjbmYiOnsiandrIjp7ImNydiI6IlAtMjU2IiwiZXh0Ijp0cnVlLCJrZXlfb3BzIjpbInZlcmlmeSJdLCJrdHkiOiJFQyIsIngiOiJjalZ6T3RUUXEwbTNqS3pfS251Ql9EUERlQ0FjbTlRcm9rM3JVTTgzUjRjIiwieSI6ImpON2dhd1dOOVFFZ1l5cENQQ19KelV6YXFxYWRyRmpoOGJXa213NUNMYjgifX0sInZjdCI6InVybjpldS5ldXJvcGEuZWMuZXVkaTpwaWQ6MSIsImp0aSI6InVybjp2aWQ6OTU2MTFhMWUtNzNjZi00ZmE3LThhMjctZjE0YzgyNTFhNTRlIiwiaWF0IjoxNzQxMTA2OTc1LCJleHAiOjE3NzI2NDI5NzUsImlzcyI6Imh0dHA6Ly93YWxsZXQtZW50ZXJwcmlzZS1pc3N1ZXI6ODAwMyIsInN1YiI6Ilhxcko1My13anNCWjNBUmlzQnJ1dmRwRk9qdnRSWGxMZzNmUWJuZmJfbVUiLCJfc2RfYWxnIjoic2hhLTI1NiIsIl9zZCI6WyItalZ2ZTM4RnNxUkRLU0hFNDlfSGJadHJYZ3VBQTFWV0dKaU00dGZCU0xZIiwiMEZ5Y1hhNzVNcTNyUHBqR1FLM0lrV3N6d0tZYmVCSnAwYndIYWl2MkcyTSIsIjRGSTAwV1JnOFFCR2ZoemtLYmdGNTM3N29DSHZ3TUlKSmFOTTVLUmNMTzgiLCI3VjN4LXhtS2MzemNBd0JVcm0ycDAzZGVkX1U2RUVmM3dqaVROd1dLeE9jIiwiOEg4M0FNQjh0NUtxVmRkcmNIbldPN0s4aUVYbXNBU0w3ZlpPd2Vnc04wMCIsIkVRMGVfcE9sOUJ0emRneWJoVnlScWxvMDdzbmo4N0Vab3h5NVIzcXktbW8iLCJGNm95UmdhSDBDalRxcVFIRjBKUVNVVGhpdnZPZV8xWEhicDdmbXFSWHlzIiwiSEMyRXR2cGl3MktVd0NSOE12N3ZpZU9obzV2Vk5WQVJrdVFzeW9kR3lTZyIsIk90MDAtTjhMNG1sdTF5cXJNTHhfTmZCT29RT1FSaWdYQWhnX0Q4STlFYnMiLCJfdm5NYU5WZ0JrTUpybmd0UEZwVkpsZ0h5djFPdUhiZWRSU2pWVWk1SVRzIiwiZGUzbWhlMHZCV0NVWkxfdThrNi1CN2hlb0NqOHYwaG9BUjFISUl5ajBUayIsImZlWnd1bkdERG8xdi13dE1XNUpwX21TUnNfbmM1MC01UGYwUmlrTXhpaFEiLCJtV1BzRF96UWFLclBNS1NkdEhocTNmR0xKaVJOenlWdEFWVjZ3U2RUZEtVIiwicTR5UWZJMl9NWVVKcGl6VWF3bUNCczlYYnBNVk43NjNueFZ5aHBQR1M1TSIsInJGUW9QSVJ5OFM1R3BkWjR5OTRSTV9NUUpDQ3Mzamh4anE1SDZNYzFIU2MiLCJyYzd4a1hEcVk3c3VBYlg3MmpLMC12ZEpNRi1tem5ZZXpkR1ktMzBiblZBIiwidER4NU1vODNtWXpNbElVZlpVaDgxdWZORTNydzZRa0dCNjRidEdqRmtBYyJdfQ.-B1l8lRrWnvVFwtMW4AC02wNR6DxHmAwPkbHeLAGd_GFqbx7SiKPgNUNw-xr1A8U73yIxC4iAWXt3CLFn4GKHg~WyJNX1ViMUZWYnphTjhVUEV1QlZ2VkN3IiwiZ2l2ZW5fbmFtZSIsIkpvaG4iXQ~WyJGNVhBVE1LNlBjY1ZNeXcwcUs5VmhnIiwiZmFtaWx5X25hbWUiLCJEb2UiXQ~WyJEcEpiWlZuSWVXY3JEekJCNlUtaDVnIiwiYmlydGhfZGF0ZSIsIjE5OTAtMTAtMTUiXQ~eyJ0eXAiOiJrYitqd3QiLCJhbGciOiJFUzI1NiJ9.eyJub25jZSI6IjlhMGEwNmQwLTA1NDctNDEwNi1iNGM2LTUxMTkzN2ViMDQ3ZiIsImF1ZCI6IndhbGxldC1lbnRlcnByaXNlLWFjbWUtdmVyaWZpZXIiLCJzZF9oYXNoIjoiem1jbXJEaG5USWFiTjh4WVlEbTVBOXpWalhyT25sVXNYLUM1RVh1ckFvOCIsImlhdCI6MTc0MTEwODE3Nn0.3dpDrheXKJe0tOEaU1JWsBRHQOuDsqkq6JBPzf5TFvOo3eFd4JgBMJAiUrMJDpbcaVLgBiCOyIEeXbscE03tQw`; /** * This certificate was used to sign the issuer's certificate */ -const rootCert = `-----BEGIN CERTIFICATE----- -MIICeTCCAiCgAwIBAgIUB5E9QVZtmUYcDtCjKB/H3VQv72gwCgYIKoZIzj0EAwIwgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMB4XDTI0MDUzMTA2NDgwOVoXDTM0MDUyOTA2NDgwOVowgYgxCzAJBgNVBAYTAkRFMQ8wDQYDVQQHDAZCZXJsaW4xHTAbBgNVBAoMFEJ1bmRlc2RydWNrZXJlaSBHbWJIMREwDwYDVQQLDAhUIENTIElERTE2MDQGA1UEAwwtU1BSSU5EIEZ1bmtlIEVVREkgV2FsbGV0IFByb3RvdHlwZSBJc3N1aW5nIENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYGzdwFDnc7+Kn5ibAvCOM8ke77VQxqfMcwZL8IaIA+WCROcCfmY/giH92qMru5p/kyOivE0RC/IbdMONvDoUyaNmMGQwHQYDVR0OBBYEFNRWGMCJOOgOWIQYyXZiv6u7xZC+MB8GA1UdIwQYMBaAFNRWGMCJOOgOWIQYyXZiv6u7xZC+MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA0cAMEQCIGEm7wkZKHt/atb4MdFnXW6yrnwMUT2u136gdtl10Y6hAiBuTFqvVYth1rbxzCP0xWZHmQK9kVyxn8GPfX27EIzzsw== ------END CERTIFICATE-----` const invalidRootCert = `-----BEGIN CERTIFICATE----- MIICdDCCAhugAwIBAgIBAjAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCREUxDzANBgNVBAcMBkJlcmxpbjEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxETAPBgNVBAsMCFQgQ1MgSURFMTYwNAYDVQQDDC1TUFJJTkQgRnVua2UgRVVESSBXYWxsZXQgUHJvdG90eXBlIElzc3VpbmcgQ0EwHhcNMjQwNTMxMDgxMzE3WhcNMjUwNzA1MDgxMzE3WjBsMQswCQYDVQQGEwJERTEdMBsGA1UECgwUQnVuZGVzZHJ1Y2tlcmVpIEdtYkgxCjAIBgNVBAsMAUkxMjAwBgNVBAMMKVNQUklORCBGdW5rZSBFVURJIFdhbGxldCBQcm90b3R5cGUgSXNzdWVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOFBq4YMKg4w5fTifsytwBuJf/7E7VhRPXiNm52S3q1ETIgBdXyDK3kVxGxgeHPivLP3uuMvS6iDEc7qMxmvduKOBkDCBjTAdBgNVHQ4EFgQUiPhCkLErDXPLW2/J0WVeghyw+mIwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwLQYDVR0RBCYwJIIiZGVtby5waWQtaXNzdWVyLmJ1bmRlc2RydWNrZXJlaS5kZTAfBgNVHSMEGDAWgBTUVhjAiTjoDliEGMl2Yr+ru8WQvjAKBggqhkjOPQQDAgNHADBEAiAbf5TzkcQzhfWoIoyi1VN7d8I9BsFKm1MWluRph2byGQIgKYkdrNf2xXPjVSbjW/U/5S5vAEC5XxcOanusOBroBbU= ------END CERTIFICATE-----` - - -const walletEnterpriseIssuerCaCertificate = `-----BEGIN CERTIFICATE----- -MIICCTCCAa+gAwIBAgIUOgCPRPz+xuyaJVSj4+pw5DL2pcswCgYIKoZIzj0EAwIw -UjELMAkGA1UEBhMCR1IxDzANBgNVBAgMBkdyZWVjZTEPMA0GA1UEBwwGQXRoZW5z -MQ4wDAYDVQQKDAVHVW5ldDERMA8GA1UEAwwId3dXYWxsZXQwHhcNMjUwMjI2MTUx -MjIwWhcNMzUwMjI0MTUxMjIwWjBSMQswCQYDVQQGEwJHUjEPMA0GA1UECAwGR3Jl -ZWNlMQ8wDQYDVQQHDAZBdGhlbnMxDjAMBgNVBAoMBUdVbmV0MREwDwYDVQQDDAh3 -d1dhbGxldDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNBZtjDVXcHAETA8F1Wj -ScHMtRtfNgYZxYb+6Q5qrOBBRpT75BvaANNoASnnXSXe8HJ4HCB9XG6UuuIhuK/X -eUejYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQW -BBR8ePhvPK+ji6cfKwa36k1rRewFojAfBgNVHSMEGDAWgBR8ePhvPK+ji6cfKwa3 -6k1rRewFojAKBggqhkjOPQQDAgNIADBFAiEAiVcSsLpcK6bkYkq03gzejgBQvvKv -nLP+NsILFBXC+I8CIHSpT1vB/tMaJKbIizZwxyOru6N/iUkpHGzVnxU5Wgu4 -----END CERTIFICATE-----`; -const context: Context = { - clockTolerance: 0, - lang: 'en-US', - subtle: crypto.subtle, - trustedCertificates: [ - rootCert, - walletEnterpriseIssuerCaCertificate - ], -}; - -describe("The SDJWTVerifier", () => { - const pkResolverEngine = PublicKeyResolverEngine(); +describe("The SDJWTVerifier", () => { it("should handle the case where the input is not an SDJWT", async () => { + const pkResolverEngine = PublicKeyResolverEngine(); + pkResolverEngine.register({ resolve: () => { + return { + success: true, + value: { + kty: 'EC', + x: 'VwNK5WDL2D9AdvBP6cLzgFwmmJIYW--uWWdqB3sIIPY', + y: 'Z7_6W1YQTyJ32RF6oGvDXM_hVYyUFWGQNK5jqw7sgXY', + crv: 'P-256' + } + } + }}); + const context: Context = { + clockTolerance: 0, + lang: 'en-US', + subtle: crypto.subtle, + trustedCertificates: [], + }; const result = await SDJWTVCVerifier({ context, pkResolverEngine }) .verify({ rawCredential: wrongFormatCredential, opts: {} @@ -61,23 +64,124 @@ describe("The SDJWTVerifier", () => { assert(result.error === CredentialVerificationError.InvalidFormat); }); - it("should successfully verify credential issued by Wallet Enterprise Issuer", async () => { - const result = await SDJWTVCVerifier({ context, pkResolverEngine }) + it("should successfully example credential verify credential issued by Wallet Enterprise Issuer", async () => { + const { sdJwt, certPem } = await sdJwtFixture(); + const resolverEngine = PublicKeyResolverEngine(); + resolverEngine.register({ resolve: () => { + return { + success: true, + value: exampleCert + } + }}); + const result = await SDJWTVCVerifier({ + context: { + clockTolerance: 0, + lang: 'en-US', + subtle: crypto.subtle, + trustedCertificates: [ + exampleCert + ], + }, + pkresolverEngine: resolverEngine + }) + .verify({ + rawCredential: exampleCredential, opts: {} + }); + + assert(result.success === true); + }); + + ['urn:eu.europa.ec.eudi:pid:1', 'urn:eudi:pid:1', 'urn:eudi:ehic:1'].forEach(vct => { + it(`should successfully verify ${vct} credential issued by Wallet Enterprise Issuer`, async () => { + const { sdJwt, certPem } = await sdJwtFixture(vct); + const resolverEngine = PublicKeyResolverEngine(); + resolverEngine.register({ resolve: () => { + return { + success: true, + value: certPem + } + }}); + const result = await SDJWTVCVerifier({ + context: { + clockTolerance: 0, + lang: 'en-US', + subtle: crypto.subtle, + trustedCertificates: [ + certPem + ], + }, + pkresolverEngine: resolverEngine + }) .verify({ - rawCredential: sdJwtCredentialIssuedByWalletEnterprise, opts: {} + rawCredential: sdJwt, opts: {} }); + assert(result.success === true); + }); + }); + + it.skip("should successfully verify urn:eudi:pda1:1 credential issued by Wallet Enterprise Issuer", async () => { + const { sdJwt, certPem } = await sdJwtFixture('urn:eudi:pda1:1'); + const resolverEngine = PublicKeyResolverEngine(); + resolverEngine.register({ resolve: () => { + return { + success: true, + value: certPem + } + }}); + const result = await SDJWTVCVerifier({ + context: { + clockTolerance: 0, + lang: 'en-US', + subtle: crypto.subtle, + trustedCertificates: [ + certPem + ], + }, + pkresolverEngine: resolverEngine + }) + .verify({ + rawCredential: sdJwt, opts: {} + }); + + assert(result.success === true); + }); + + it.skip("should successfully verify urn:eu.europa.ec.eudi:por:1 credential issued by Wallet Enterprise Issuer", async () => { + const { sdJwt, certPem } = await sdJwtFixture('urn:eu.europa.ec.eudi:por:1'); + const resolverEngine = PublicKeyResolverEngine(); + resolverEngine.register({ resolve: () => { + return { + success: true, + value: certPem + } + }}); + const result = await SDJWTVCVerifier({ + context: { + clockTolerance: 0, + lang: 'en-US', + subtle: crypto.subtle, + trustedCertificates: [ + certPem + ], + }, + pkresolverEngine: resolverEngine + }) + .verify({ + rawCredential: sdJwt, opts: {} + }); + assert(result.success === true); }); - it("should successfully verify SDJWT+KBJWT issued by Wallet Enterprise Issuer", async () => { + it.skip("should successfully verify SDJWT+KBJWT issued by Wallet Enterprise Issuer", async () => { const result = await SDJWTVCVerifier({ context, pkResolverEngine }) - .verify({ - rawCredential: sdJwtCredentialIssuedByWalletEnterpriseWithKbJwt, opts: { - expectedNonce: "9a0a06d0-0547-4106-b4c6-511937eb047f", - expectedAudience: "wallet-enterprise-acme-verifier", - } - }); + .verify({ + rawCredential: sdJwtCredentialIssuedByWalletEnterpriseWithKbJwt, opts: { + expectedNonce: "9a0a06d0-0547-4106-b4c6-511937eb047f", + expectedAudience: "wallet-enterprise-acme-verifier", + } + }); assert(result.success === true); }); diff --git a/src/credential-verifiers/SDJWTVCVerifier.ts b/src/credential-verifiers/SDJWTVCVerifier.ts index d13db08..5e15db7 100644 --- a/src/credential-verifiers/SDJWTVCVerifier.ts +++ b/src/credential-verifiers/SDJWTVCVerifier.ts @@ -1,12 +1,23 @@ +import axios from "axios" import { SDJwt } from "@sd-jwt/core"; +import { SDJwtVcInstance } from "@sd-jwt/sd-jwt-vc"; import type { HasherAndAlg } from "@sd-jwt/types"; import { Context, CredentialVerifier, PublicKeyResolverEngineI } from "../interfaces"; import { CredentialVerificationError } from "../error"; -import { Result } from "../types"; +import { Result, Vct, VctUrls } from "../types"; import { exportJWK, importJWK, importX509, JWK, jwtVerify, KeyLike } from "jose"; import { fromBase64Url, toBase64Url } from "../utils/util"; import { verifyCertificate } from "../utils/verifyCertificate"; + +const VctUrls: VctUrls = { + 'urn:eu.europa.ec.eudi:pid:1': 'https://demo-issuer.wwwallet.org/public/creds/pid/person-identification-data-arf-15-vctm-example-01.json', + 'urn:eudi:pid:1': 'https://demo-issuer.wwwallet.org/public/creds/pid/person-identification-data-arf-18-vctm-example-01.json', + 'urn:eudi:ehic:1': 'https://demo-issuer.wwwallet.org/public/creds/ehic/european-health-insurance-card-vctm-dc4eu-01.json', + 'urn:eudi:pda1:1': 'https://demo-issuer.wwwallet.org/public/creds/pda1/portable-document-a1-vctm-dc4eu-01.json', + 'urn:eu.europa.ec.eudi:por:1': 'https://demo-issuer.wwwallet.org/public/creds/por/power-of-representation-vctm-potential-01.json', +}; + export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: PublicKeyResolverEngineI }): CredentialVerifier { let errors: { error: CredentialVerificationError, message: string }[] = []; const logError = (error: CredentialVerificationError, message: string): void => { @@ -129,7 +140,6 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ error: CredentialVerificationError.CannotImportIssuerPublicKey, } } - } if (parsedSdJwt && parsedSdJwt.payload && typeof parsedSdJwt.payload.iss === 'string' && typeof alg === 'string') { const publicKeyResolutionResult = await args.pkResolverEngine.resolve({ identifier: parsedSdJwt.payload.iss }); @@ -198,6 +208,33 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ } } + const verifyCredentialVct = async (rawCredential: string): Promise> => { + const SdJwtVc = new SDJwtVcInstance({ + verifier: () => true, + hasher: hasherAndAlgorithm.hasher, + hashAlg: hasherAndAlgorithm.alg as 'sha-256', + loadTypeMetadataFormat: true, + vctFetcher: (urn) => { + const url = VctUrls[urn as Vct] + return axios.get(url).then(({ data }) => data) + } + }); + + const verified = await SdJwtVc.verify(rawCredential); + + if (!verified.payload) { + return { + success: false, + error: CredentialVerificationError.VctSchemaError, + } + } + + return { + success: true, + value: {}, + } + } + const verifyKbJwt = async (rawPresentation: string, opts: { expectedNonce?: string; expectedAudience?: string; @@ -289,6 +326,15 @@ export function SDJWTVCVerifier(args: { context: Context, pkResolverEngine: Publ } } + // Credential vct validation + const credentialVctVerificationResult = await verifyCredentialVct(rawCredential); + if (!credentialVctVerificationResult.success) { + return { + success: false, + error: errors.length > 0 ? errors[0].error : CredentialVerificationError.UnknownProblem, + } + } + // KB-JWT validation if (!rawCredential.endsWith('~')) { // contains kbjwt const verifyKbJwtResult = await verifyKbJwt(rawCredential, opts); diff --git a/src/error.ts b/src/error.ts index 25295e6..907e44c 100644 --- a/src/error.ts +++ b/src/error.ts @@ -34,6 +34,7 @@ export enum CredentialVerificationError { CannotResolveIssuerPublicKey = "CannotResolveIssuerPublicKey", CannotImportIssuerPublicKey = "CannotImportIssuerPublicKey", NotTrustedIssuer = "NotTrustedIssuer", + VctSchemaError = "VctSchemaError", ExpiredCredential = "ExpiredCredential", diff --git a/src/types.ts b/src/types.ts index 962cdf8..7894649 100644 --- a/src/types.ts +++ b/src/types.ts @@ -42,3 +42,9 @@ export type ParsedCredential = { } signedClaims: CredentialClaims, }; + +export type Vct = 'urn:eu.europa.ec.eudi:pid:1' | 'urn:eudi:pid:1' | 'urn:eudi:ehic:1' | 'urn:eudi:pda1:1' | 'urn:eu.europa.ec.eudi:por:1'; + +export type VctUrls = { + [k in Vct]: string +}; diff --git a/test/fixtures.ts b/test/fixtures.ts new file mode 100644 index 0000000..10216d4 --- /dev/null +++ b/test/fixtures.ts @@ -0,0 +1,147 @@ +import fs from 'node:fs' +import Crypto from 'node:crypto' +import { exec } from 'child_process' +import { SignJWT } from 'jose' + +function generateCertificate () { + return new Promise(resolve => { + exec('openssl ecparam -name prime256v1 -genkey -noout -out ./test/fixtures/cert.key', () => { + exec('openssl req -new -x509 -key ./test/fixtures/cert.key -out ./test/fixtures/cert.pem -days 3650 -subj /CN=test/C=FR/ST=test/L=test/O=test', () => { + return resolve('ok'); + }); + }); + }); +} + +const vctClaims = { + "urn:eu.europa.ec.eudi:pid:1": { + "expiry_date": "2035-04-21", + "birth_place": "US", + "birth_date": "1990-10-15", + "issuance_date": "2025-03-04", + "document_number": "12313213", + "nationality": ["US"], + "email_address": "john@sample.com", + "age_over_18": false, + "mobile_phone_number": "+308388338382", + "resident_address": "23, Random str. 34793 Apt 3 USA", + "given_name_birth": "John", + "given_name": "John", + "family_name": "Doe", + "issuing_authority": "PID:00001", + "issuing_country": "GR", + "sex": 1, + }, + "urn:eudi:pid:1": { + "family_name": "test", + "given_name": "test", + "birthdate": "1923-12-25", + "place_of_birth": { + "locality": "test", + "region": "test", + "country": "GR", + }, + "nationalities": ["GR"], + "personal_administrative_number": "test", + "birth_given_name": "test", + "sex": 0, + "email": "test@test.test", + "date_of_expiry": "1925-01-17", + "issuing_authority": "test", + "issuing_country": "GR", + }, + "urn:eudi:ehic:1": { + "personal_administrative_number": "test", + "issuing_authority": { + "id": "test", + "name": "test", + }, + "issuing_country": "GR", + "date_of_expiry": "1925-01-17", + "document_number": "test", + }, + "urn:eudi:pda1:1": { + "personal_administrative_number": "test", + "employer": "test", + "work_address": { + "formatted": "", + "street_address": "", + }, + "legislation_country": "GR", + "issuing_authority": { + "id": "test", + "name": "test", + }, + "issuing_country": "GR", + "date_of_expiry": "1925-01-17", + "date_of_issuance": "1925-01-17", + "document_number": "test", + }, + "urn:eu.europa.ec.eudi:por:1": { + "legal_person_identifier": "test", + } +}; + +export function sdJwtFixture (vct: string = 'urn:eu.europa.ec.eudi:pid:1') { + const claims = vctClaims[vct]; + + return new Promise(async resolve => { + await generateCertificate(); + const certPem = fs.readFileSync('./test/fixtures/cert.pem').toString('utf8'); + const privateKeyPem = fs.readFileSync('./test/fixtures/cert.key').toString('utf8'); + + const cert = Crypto.createPublicKey(certPem); + const privateKey = Crypto.createPrivateKey(privateKeyPem); + const x5c = [ + certPem + .replace('-----BEGIN CERTIFICATE-----\n', '') + .replace('\n-----END CERTIFICATE-----\n', '') + ]; + + const header = { + "typ": "vc+sd-jwt", + "vctm": [ + "eyJ2Y3QiOiJ1cm46ZXUuZXVyb3BhLmVjLmV1ZGk6cGlkOjEiLCJuYW1lIjoiVmVyaWZpYWJsZSBJRCIsImRlc2NyaXB0aW9uIjoiVGhpcyBpcyBhIFZlcmlmaWFibGUgSUQgZG9jdW1lbnQgaXNzdWVkIGJ5IHRoZSB3ZWxsIGtub3duIFZJRCBJc3N1ZXIiLCJkaXNwbGF5IjpbeyJsYW5nIjoiZW4tVVMiLCJuYW1lIjoiVmVyaWZpYWJsZSBJRCIsInJlbmRlcmluZyI6eyJzaW1wbGUiOnsibG9nbyI6eyJ1cmkiOiJodHRwOi8vd2FsbGV0LWVudGVycHJpc2UtaXNzdWVyOjgwMDMvaW1hZ2VzL2xvZ28ucG5nIiwidXJpI2ludGVncml0eSI6InNoYTI1Ni1hY2RhMzQwNGMyY2Y0NmRhMTkyY2YyNDVjY2M2YjkxZWRjZTg4NjkxMjJmYTVhNjYzNjI4NGYxYTYwZmZjZDg2IiwiYWx0X3RleHQiOiJWSUQgTG9nbyJ9LCJiYWNrZ3JvdW5kX2NvbG9yIjoiIzRjYzNkZCIsInRleHRfY29sb3IiOiIjRkZGRkZGIn0sInN2Z190ZW1wbGF0ZXMiOlt7InVyaSI6Imh0dHA6Ly93YWxsZXQtZW50ZXJwcmlzZS1pc3N1ZXI6ODAwMy9pbWFnZXMvdGVtcGxhdGUtcGlkLnN2ZyJ9XX19XSwiY2xhaW1zIjpbeyJwYXRoIjpbImdpdmVuX25hbWUiXSwiZGlzcGxheSI6W3sibGFuZyI6ImVuLVVTIiwibGFiZWwiOiJHaXZlbiBOYW1lIiwiZGVzY3JpcHRpb24iOiJUaGUgZ2l2ZW4gbmFtZSBvZiB0aGUgVklEIGhvbGRlciJ9XSwic3ZnX2lkIjoiZ2l2ZW5fbmFtZSJ9LHsicGF0aCI6WyJmYW1pbHlfbmFtZSJdLCJkaXNwbGF5IjpbeyJsYW5nIjoiZW4tVVMiLCJsYWJlbCI6IkZhbWlseSBOYW1lIiwiZGVzY3JpcHRpb24iOiJUaGUgZmFtaWx5IG5hbWUgb2YgdGhlIFZJRCBob2xkZXIifV0sInN2Z19pZCI6ImZhbWlseV9uYW1lIn0seyJwYXRoIjpbImJpcnRoX2RhdGUiXSwiZGlzcGxheSI6W3sibGFuZyI6ImVuLVVTIiwibGFiZWwiOiJCaXJ0aCBkYXRlIiwiZGVzY3JpcHRpb24iOiJUaGUgYmlydGggZGF0ZSBvZiB0aGUgVklEIGhvbGRlciJ9XSwic3ZnX2lkIjoiYmlydGhfZGF0ZSJ9LHsicGF0aCI6WyJpc3N1aW5nX2F1dGhvcml0eSJdLCJkaXNwbGF5IjpbeyJsYW5nIjoiZW4tVVMiLCJsYWJlbCI6Iklzc3VpbmcgYXV0aG9yaXR5IiwiZGVzY3JpcHRpb24iOiJUaGUgaXNzdWluZyBhdXRob3JpdHkgb2YgdGhlIFZJRCBjcmVkZW50aWFsIn1dLCJzdmdfaWQiOiJpc3N1aW5nX2F1dGhvcml0eSJ9LHsicGF0aCI6WyJpc3N1YW5jZV9kYXRlIl0sImRpc3BsYXkiOlt7ImxhbmciOiJlbi1VUyIsImxhYmVsIjoiSXNzdWFuY2UgZGF0ZSIsImRlc2NyaXB0aW9uIjoiVGhlIGRhdGUgdGhhdCB0aGUgY3JlZGVudGlhbCB3YXMgaXNzdWVkIn1dLCJzdmdfaWQiOiJpc3N1YW5jZV9kYXRlIn0seyJwYXRoIjpbImV4cGlyeV9kYXRlIl0sImRpc3BsYXkiOlt7ImxhbmciOiJlbi1VUyIsImxhYmVsIjoiSXNzdWFuY2UgZGF0ZSIsImRlc2NyaXB0aW9uIjoiVGhlIGRhdGUgdGhhdCB0aGUgY3JlZGVudGlhbCB3aWxsIGV4cGlyZSJ9XSwic3ZnX2lkIjoiZXhwaXJ5X2RhdGUifV19" + ], + "x5c": [ + x5c + ], + "alg": "ES256" + }; + + + const disclosures = Object.keys(claims).map(key => { + const salt = (Math.random() + 1).toString(36) + const rawDisclosure = [salt, key, claims[key]] + const disclosure = Buffer.from(JSON.stringify(rawDisclosure)).toString('base64url') + + const hash = Crypto.createHash('sha256') + const _sd = hash.update(disclosure).digest('base64url') + + + return { _sd, disclosure, rawDisclosure } + }); + + const body = { + "cnf": { + "jwk": cert.export({ format: 'jwk' }) + }, + "vct": vct, + "jti": "urn:vid:95611a1e-73cf-4fa7-8a27-f14c8251a54e", + "iat": 1741106975, + "exp": 1772642975, + "iss": "http://wallet-enterprise-issuer:8003", + "sub": "XqrJ53-wjsBZ3ARisBruvdpFOjvtRXlLg3fQbnfb_mU", + "_sd_alg": "sha-256", + "_sd": disclosures.map(({ _sd }) => _sd) + } + + + const jwt = await new SignJWT(body) + .setProtectedHeader(header) + .sign(privateKey); + const sdJwt = jwt + '~' + disclosures.map(({ disclosure }) => disclosure).join('~') + '~'; + + return resolve({ sdJwt, privateKey, cert, certPem }); + }); +} diff --git a/yarn.lock b/yarn.lock index 50babf9..0491c55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -421,7 +421,7 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.0.tgz#1a40b4792c08094b6479c48c90fe7f4b10ec2f54" integrity sha512-5hqO5S3PTEO2E5VjCePxv40gIgyS2KvO7E7/vvC/NbIW4SIRamkMr1hqj+5Y67fbBWv/bQLB6KelBQmXlyCjWA== -"@sd-jwt/core@^0.10.0": +"@sd-jwt/core@0.10.0", "@sd-jwt/core@^0.10.0": version "0.10.0" resolved "https://registry.yarnpkg.com/@sd-jwt/core/-/core-0.10.0.tgz#74a1b6e017423266d94302f907c4d3f2792b3b64" integrity sha512-EuFsIHP76fwNi97dGcz2jdEenHL/AkDGcqrEA00k82Uw0HP/hvbAfB+yyPxYrd3dVaxe5PWSKvDkgDK6kKk+6Q== @@ -439,6 +439,15 @@ "@sd-jwt/types" "0.10.0" "@sd-jwt/utils" "0.10.0" +"@sd-jwt/jwt-status-list@0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/jwt-status-list/-/jwt-status-list-0.10.0.tgz#68743a65481b863df64da10246e2e7c42fd691bd" + integrity sha512-TVLLQ7qjZKKEXODXUAxoWvJmnH3bSNXsttp0hxVP/qv42Rk7OFKIjepkzy6lh+RQRWw3NK/rHiz95/Fz1m4xBQ== + dependencies: + "@sd-jwt/types" "0.10.0" + base64url "^3.0.1" + pako "^2.1.0" + "@sd-jwt/present@0.10.0": version "0.10.0" resolved "https://registry.yarnpkg.com/@sd-jwt/present/-/present-0.10.0.tgz#a4f12c1d0f5172be4a007fba447561033f20a5a5" @@ -448,6 +457,17 @@ "@sd-jwt/types" "0.10.0" "@sd-jwt/utils" "0.10.0" +"@sd-jwt/sd-jwt-vc@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@sd-jwt/sd-jwt-vc/-/sd-jwt-vc-0.10.0.tgz#d8c31ca412c78ee9a0af776d2bd8c9ef6c1778c1" + integrity sha512-418TX436dBK7FnwsLJXUXnTQByZQOonCuJ4vKyrOJ27mZXxkiFiLSaUojqoFerSw5u29nwrcRRJzd1NLdLWqkQ== + dependencies: + "@sd-jwt/core" "0.10.0" + "@sd-jwt/jwt-status-list" "0.10.0" + "@sd-jwt/utils" "0.10.0" + ajv "^8.17.1" + ajv-formats "^3.0.1" + "@sd-jwt/types@0.10.0", "@sd-jwt/types@^0.10.0": version "0.10.0" resolved "https://registry.yarnpkg.com/@sd-jwt/types/-/types-0.10.0.tgz#427e60a8a09b194e99c0a0497546bd39a3eb79bd" @@ -537,7 +557,14 @@ loupe "^3.1.2" tinyrainbow "^1.2.0" -ajv@^8.17.1: +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + +ajv@^8.0.0, ajv@^8.17.1: version "8.17.1" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== @@ -580,6 +607,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -825,6 +857,11 @@ node-gyp-build-optional-packages@5.1.1: dependencies: detect-libc "^2.0.1" +pako@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== + pathe@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.2.tgz#6c4cb47a945692e48a1ddd6e4094d170516437ec"