@blamejs/blamejs-shop 0.5.12 → 0.5.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/lib/asset-manifest.json +1 -1
- package/lib/vendor/MANIFEST.json +632 -416
- package/lib/vendor/blamejs/.clusterfuzzlite/Dockerfile +21 -10
- package/lib/vendor/blamejs/.clusterfuzzlite/build.sh +30 -9
- package/lib/vendor/blamejs/.github/dependabot.yml +8 -0
- package/lib/vendor/blamejs/.github/workflows/actions-lint.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/cflite_batch.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/cflite_pr.yml +7 -2
- package/lib/vendor/blamejs/.github/workflows/ci.yml +21 -21
- package/lib/vendor/blamejs/.github/workflows/codeql.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/npm-publish.yml +4 -4
- package/lib/vendor/blamejs/.github/workflows/release-container.yml +7 -7
- package/lib/vendor/blamejs/.github/workflows/scorecard.yml +3 -3
- package/lib/vendor/blamejs/.github/workflows/sha-to-tag-verify.yml +1 -1
- package/lib/vendor/blamejs/.gitignore +2 -0
- package/lib/vendor/blamejs/CHANGELOG.md +122 -0
- package/lib/vendor/blamejs/NOTICE +26 -1
- package/lib/vendor/blamejs/SECURITY.md +1 -1
- package/lib/vendor/blamejs/api-snapshot.json +207 -5
- package/lib/vendor/blamejs/docker/keycloak/realm-blamejs-test.json +29 -0
- package/lib/vendor/blamejs/examples/wiki/DEPLOY.md +1 -0
- package/lib/vendor/blamejs/examples/wiki/lib/build-app.js +31 -0
- package/lib/vendor/blamejs/examples/wiki/lib/source-comment-block-validator.js +109 -0
- package/lib/vendor/blamejs/examples/wiki/nginx.conf +106 -0
- package/lib/vendor/blamejs/examples/wiki/package-lock.json +1 -1
- package/lib/vendor/blamejs/examples/wiki/test/e2e.js +92 -0
- package/lib/vendor/blamejs/fuzz/asn1-der.fuzz.js +28 -0
- package/lib/vendor/blamejs/fuzz/cms-codec.fuzz.js +17 -0
- package/lib/vendor/blamejs/fuzz/link-header.fuzz.js +18 -0
- package/lib/vendor/blamejs/index.js +6 -0
- package/lib/vendor/blamejs/lib/a2a.js +4 -4
- package/lib/vendor/blamejs/lib/agent-idempotency.js +18 -2
- package/lib/vendor/blamejs/lib/agent-saga.js +36 -8
- package/lib/vendor/blamejs/lib/agent-snapshot.js +63 -6
- package/lib/vendor/blamejs/lib/app-shutdown.js +23 -20
- package/lib/vendor/blamejs/lib/archive-read.js +8 -0
- package/lib/vendor/blamejs/lib/archive-tar.js +12 -2
- package/lib/vendor/blamejs/lib/arg-parser.js +49 -5
- package/lib/vendor/blamejs/lib/asn1-der.js +12 -0
- package/lib/vendor/blamejs/lib/audit-sign.js +47 -1
- package/lib/vendor/blamejs/lib/audit.js +71 -8
- package/lib/vendor/blamejs/lib/auth/ciba.js +14 -2
- package/lib/vendor/blamejs/lib/auth/fido-mds3.js +58 -47
- package/lib/vendor/blamejs/lib/auth/jwt.js +34 -0
- package/lib/vendor/blamejs/lib/auth/oauth.js +90 -13
- package/lib/vendor/blamejs/lib/auth/oid4vci.js +20 -17
- package/lib/vendor/blamejs/lib/auth/openid-federation.js +214 -23
- package/lib/vendor/blamejs/lib/auth/saml.js +38 -20
- package/lib/vendor/blamejs/lib/auth/sd-jwt-vc.js +10 -4
- package/lib/vendor/blamejs/lib/backup/bundle.js +13 -3
- package/lib/vendor/blamejs/lib/backup/index.js +16 -5
- package/lib/vendor/blamejs/lib/backup/manifest.js +18 -5
- package/lib/vendor/blamejs/lib/base32.js +21 -0
- package/lib/vendor/blamejs/lib/break-glass.js +19 -10
- package/lib/vendor/blamejs/lib/bundler.js +5 -1
- package/lib/vendor/blamejs/lib/cache-status.js +3 -7
- package/lib/vendor/blamejs/lib/calendar.js +21 -1
- package/lib/vendor/blamejs/lib/cbor.js +12 -1
- package/lib/vendor/blamejs/lib/cert.js +29 -11
- package/lib/vendor/blamejs/lib/cli.js +94 -24
- package/lib/vendor/blamejs/lib/cloud-events.js +2 -2
- package/lib/vendor/blamejs/lib/cluster-provider-db.js +13 -3
- package/lib/vendor/blamejs/lib/codepoint-class.js +85 -0
- package/lib/vendor/blamejs/lib/compliance-ai-act-prohibited.js +8 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act-transparency.js +19 -4
- package/lib/vendor/blamejs/lib/compliance-ai-act.js +6 -8
- package/lib/vendor/blamejs/lib/content-credentials.js +1 -1
- package/lib/vendor/blamejs/lib/content-digest.js +2 -1
- package/lib/vendor/blamejs/lib/cookies.js +13 -3
- package/lib/vendor/blamejs/lib/cose.js +31 -16
- package/lib/vendor/blamejs/lib/crypto-field.js +32 -0
- package/lib/vendor/blamejs/lib/crypto-oprf.js +11 -2
- package/lib/vendor/blamejs/lib/crypto-xwing.js +22 -4
- package/lib/vendor/blamejs/lib/crypto.js +314 -19
- package/lib/vendor/blamejs/lib/csp.js +11 -3
- package/lib/vendor/blamejs/lib/daemon.js +270 -24
- package/lib/vendor/blamejs/lib/dark-patterns.js +5 -2
- package/lib/vendor/blamejs/lib/db-declare-view.js +2 -2
- package/lib/vendor/blamejs/lib/db-query.js +34 -7
- package/lib/vendor/blamejs/lib/db.js +18 -6
- package/lib/vendor/blamejs/lib/dbsc.js +17 -0
- package/lib/vendor/blamejs/lib/dev.js +21 -2
- package/lib/vendor/blamejs/lib/dsr.js +157 -102
- package/lib/vendor/blamejs/lib/external-db.js +41 -2
- package/lib/vendor/blamejs/lib/file-upload.js +25 -7
- package/lib/vendor/blamejs/lib/flag-providers.js +27 -15
- package/lib/vendor/blamejs/lib/forms.js +113 -27
- package/lib/vendor/blamejs/lib/gate-contract.js +23 -10
- package/lib/vendor/blamejs/lib/gdpr-ropa.js +8 -1
- package/lib/vendor/blamejs/lib/guard-agent-registry.js +1 -1
- package/lib/vendor/blamejs/lib/guard-archive.js +2 -2
- package/lib/vendor/blamejs/lib/guard-cidr.js +27 -1
- package/lib/vendor/blamejs/lib/guard-graphql.js +30 -11
- package/lib/vendor/blamejs/lib/guard-html.js +26 -53
- package/lib/vendor/blamejs/lib/guard-image.js +24 -5
- package/lib/vendor/blamejs/lib/guard-imap-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-jmap.js +19 -3
- package/lib/vendor/blamejs/lib/guard-jwt.js +18 -2
- package/lib/vendor/blamejs/lib/guard-list-unsubscribe.js +2 -2
- package/lib/vendor/blamejs/lib/guard-managesieve-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-markdown.js +11 -24
- package/lib/vendor/blamejs/lib/guard-pdf.js +18 -2
- package/lib/vendor/blamejs/lib/guard-pop3-command.js +10 -2
- package/lib/vendor/blamejs/lib/guard-regex.js +8 -5
- package/lib/vendor/blamejs/lib/guard-svg.js +31 -32
- package/lib/vendor/blamejs/lib/guard-time.js +1 -1
- package/lib/vendor/blamejs/lib/guard-uuid.js +2 -2
- package/lib/vendor/blamejs/lib/html-balance.js +3 -3
- package/lib/vendor/blamejs/lib/http-client-cache.js +82 -4
- package/lib/vendor/blamejs/lib/http-client-cookie-jar.js +16 -1
- package/lib/vendor/blamejs/lib/http-client.js +39 -7
- package/lib/vendor/blamejs/lib/http-message-signature.js +35 -1
- package/lib/vendor/blamejs/lib/i18n-messageformat.js +31 -6
- package/lib/vendor/blamejs/lib/i18n.js +35 -21
- package/lib/vendor/blamejs/lib/incident-report.js +8 -1
- package/lib/vendor/blamejs/lib/ip-utils.js +11 -1
- package/lib/vendor/blamejs/lib/json-path.js +23 -1
- package/lib/vendor/blamejs/lib/jwk.js +20 -3
- package/lib/vendor/blamejs/lib/link-header.js +2 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp-grpc.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream-otlp.js +5 -1
- package/lib/vendor/blamejs/lib/log-stream.js +8 -3
- package/lib/vendor/blamejs/lib/mail-auth.js +123 -22
- package/lib/vendor/blamejs/lib/mail-bimi.js +1 -1
- package/lib/vendor/blamejs/lib/mail-bounce.js +8 -0
- package/lib/vendor/blamejs/lib/mail-crypto-pgp.js +46 -10
- package/lib/vendor/blamejs/lib/mail-dav.js +22 -3
- package/lib/vendor/blamejs/lib/mail-dkim.js +96 -30
- package/lib/vendor/blamejs/lib/mail-helo.js +6 -1
- package/lib/vendor/blamejs/lib/mail-scan.js +23 -16
- package/lib/vendor/blamejs/lib/mail-send-deliver.js +34 -8
- package/lib/vendor/blamejs/lib/mail-server-imap.js +3 -4
- package/lib/vendor/blamejs/lib/mail-server-managesieve.js +7 -11
- package/lib/vendor/blamejs/lib/mail-server-mx.js +93 -7
- package/lib/vendor/blamejs/lib/mail-server-registry.js +8 -2
- package/lib/vendor/blamejs/lib/mail.js +15 -5
- package/lib/vendor/blamejs/lib/metrics.js +268 -63
- package/lib/vendor/blamejs/lib/middleware/age-gate.js +7 -1
- package/lib/vendor/blamejs/lib/middleware/asyncapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/body-parser.js +19 -4
- package/lib/vendor/blamejs/lib/middleware/bot-guard.js +5 -9
- package/lib/vendor/blamejs/lib/middleware/csp-report.js +40 -8
- package/lib/vendor/blamejs/lib/middleware/csrf-protect.js +24 -10
- package/lib/vendor/blamejs/lib/middleware/dpop.js +10 -5
- package/lib/vendor/blamejs/lib/middleware/index.js +6 -1
- package/lib/vendor/blamejs/lib/middleware/nel.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/openapi-serve.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/rate-limit.js +12 -1
- package/lib/vendor/blamejs/lib/middleware/require-bound-key.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/scim-server.js +1 -1
- package/lib/vendor/blamejs/lib/middleware/tus-upload.js +6 -2
- package/lib/vendor/blamejs/lib/mtls-ca.js +18 -2
- package/lib/vendor/blamejs/lib/network-dns-resolver.js +11 -1
- package/lib/vendor/blamejs/lib/network-dns.js +21 -2
- package/lib/vendor/blamejs/lib/network-tls.js +69 -7
- package/lib/vendor/blamejs/lib/nonce-store.js +9 -1
- package/lib/vendor/blamejs/lib/object-store/gcs.js +7 -0
- package/lib/vendor/blamejs/lib/object-store/sigv4.js +31 -0
- package/lib/vendor/blamejs/lib/observability-otlp-exporter.js +1 -1
- package/lib/vendor/blamejs/lib/outbox.js +1 -1
- package/lib/vendor/blamejs/lib/parsers/safe-ini.js +9 -1
- package/lib/vendor/blamejs/lib/parsers/safe-toml.js +10 -1
- package/lib/vendor/blamejs/lib/permissions.js +6 -3
- package/lib/vendor/blamejs/lib/pid-probe.js +55 -0
- package/lib/vendor/blamejs/lib/pqc-agent.js +8 -1
- package/lib/vendor/blamejs/lib/pqc-software.js +3 -3
- package/lib/vendor/blamejs/lib/pubsub.js +20 -9
- package/lib/vendor/blamejs/lib/queue-local.js +8 -1
- package/lib/vendor/blamejs/lib/queue-redis.js +5 -0
- package/lib/vendor/blamejs/lib/redact.js +54 -0
- package/lib/vendor/blamejs/lib/restore-bundle.js +10 -2
- package/lib/vendor/blamejs/lib/restore.js +19 -0
- package/lib/vendor/blamejs/lib/retention.js +12 -4
- package/lib/vendor/blamejs/lib/router.js +60 -6
- package/lib/vendor/blamejs/lib/safe-buffer.js +69 -0
- package/lib/vendor/blamejs/lib/safe-decompress.js +8 -2
- package/lib/vendor/blamejs/lib/safe-dns.js +25 -20
- package/lib/vendor/blamejs/lib/safe-ical.js +7 -3
- package/lib/vendor/blamejs/lib/safe-mime.js +12 -1
- package/lib/vendor/blamejs/lib/safe-object.js +80 -0
- package/lib/vendor/blamejs/lib/safe-path.js +52 -4
- package/lib/vendor/blamejs/lib/safe-redirect.js +10 -2
- package/lib/vendor/blamejs/lib/safe-schema.js +4 -1
- package/lib/vendor/blamejs/lib/safe-sieve.js +33 -1
- package/lib/vendor/blamejs/lib/safe-sql.js +88 -0
- package/lib/vendor/blamejs/lib/safe-vcard.js +7 -3
- package/lib/vendor/blamejs/lib/scheduler.js +8 -1
- package/lib/vendor/blamejs/lib/security-assert.js +6 -6
- package/lib/vendor/blamejs/lib/self-update-standalone-verifier.js +74 -27
- package/lib/vendor/blamejs/lib/self-update.js +517 -87
- package/lib/vendor/blamejs/lib/server-timing.js +2 -4
- package/lib/vendor/blamejs/lib/session.js +34 -12
- package/lib/vendor/blamejs/lib/sql.js +15 -1
- package/lib/vendor/blamejs/lib/ssrf-guard.js +52 -0
- package/lib/vendor/blamejs/lib/static.js +55 -13
- package/lib/vendor/blamejs/lib/storage.js +8 -2
- package/lib/vendor/blamejs/lib/subject.js +6 -1
- package/lib/vendor/blamejs/lib/tenant-quota.js +52 -13
- package/lib/vendor/blamejs/lib/tls-exporter.js +3 -3
- package/lib/vendor/blamejs/lib/tsa.js +34 -13
- package/lib/vendor/blamejs/lib/uri-template.js +8 -1
- package/lib/vendor/blamejs/lib/vault/index.js +13 -0
- package/lib/vendor/blamejs/lib/vc.js +44 -5
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +26 -26
- package/lib/vendor/blamejs/lib/vendor/noble-ciphers.cjs +792 -1
- package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +3443 -1
- package/lib/vendor/blamejs/lib/vendor/noble-post-quantum.cjs +2737 -1
- package/lib/vendor/blamejs/lib/vendor/pki.cjs +39550 -13
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.dat +55 -53
- package/lib/vendor/blamejs/lib/vendor/public-suffix-list.data.js +5551 -5554
- package/lib/vendor/blamejs/lib/vendor/simplewebauthn-server.cjs +22459 -32
- package/lib/vendor/blamejs/lib/watcher.js +89 -17
- package/lib/vendor/blamejs/lib/webhook-dispatcher.js +25 -3
- package/lib/vendor/blamejs/lib/webhook.js +12 -2
- package/lib/vendor/blamejs/lib/ws-client.js +32 -1
- package/lib/vendor/blamejs/lib/xml-c14n.js +29 -1
- package/lib/vendor/blamejs/oss-fuzz/projects/blamejs/Dockerfile +1 -1
- package/lib/vendor/blamejs/package-lock.json +2 -2
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.x.json +1462 -0
- package/lib/vendor/blamejs/release-notes/v0.17.0.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.1.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.10.json +30 -0
- package/lib/vendor/blamejs/release-notes/v0.17.11.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.12.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.13.json +77 -0
- package/lib/vendor/blamejs/release-notes/v0.17.14.json +40 -0
- package/lib/vendor/blamejs/release-notes/v0.17.15.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.16.json +27 -0
- package/lib/vendor/blamejs/release-notes/v0.17.17.json +31 -0
- package/lib/vendor/blamejs/release-notes/v0.17.18.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.19.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.2.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.20.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.21.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.22.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.17.3.json +43 -0
- package/lib/vendor/blamejs/release-notes/v0.17.4.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.5.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.6.json +26 -0
- package/lib/vendor/blamejs/release-notes/v0.17.7.json +35 -0
- package/lib/vendor/blamejs/release-notes/v0.17.8.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.17.9.json +26 -0
- package/lib/vendor/blamejs/scripts/validate-source-comment-blocks.js +4 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +293 -9
- package/lib/vendor/blamejs/socket.yml +94 -0
- package/lib/vendor/blamejs/test/00-primitives.js +605 -0
- package/lib/vendor/blamejs/test/helpers/drivers.js +12 -9
- package/lib/vendor/blamejs/test/integration/audit-stack-mysql.test.js +90 -1
- package/lib/vendor/blamejs/test/integration/audit-stack-postgres.test.js +122 -1
- package/lib/vendor/blamejs/test/integration/data-layer-mysql.test.js +30 -0
- package/lib/vendor/blamejs/test/integration/data-layer-pg.test.js +29 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-mysql.test.js +479 -0
- package/lib/vendor/blamejs/test/integration/dsr-cluster-pg.test.js +487 -0
- package/lib/vendor/blamejs/test/integration/federation-auth.test.js +313 -5
- package/lib/vendor/blamejs/test/integration/mail-dkim.test.js +80 -0
- package/lib/vendor/blamejs/test/integration/openid-federation-chain.test.js +275 -0
- package/lib/vendor/blamejs/test/integration/webhook-dispatcher-pg.test.js +96 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/a2a-tasks.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/acme.test.js +1649 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/age-gate.test.js +33 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-idempotency.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-saga.test.js +60 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/agent-snapshot.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/api-encrypt.test.js +891 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/app-shutdown.test.js +128 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-adapters.test.js +271 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-read.test.js +628 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-tar-read.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/archive-wrap.test.js +227 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/arg-parser.test.js +15 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/asn1-der.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/atomic-file.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-sign-anchor.test.js +141 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit-tools.test.js +624 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/audit.test.js +769 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-jwt-defenses.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth.test.js +1697 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oid4vp.test.js +495 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-password.test.js +500 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml.test.js +1809 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-status-list.test.js +243 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index.test.js +2226 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-manifest-signature.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/base32.test.js +105 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/break-glass.test.js +693 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cache.test.js +603 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/calendar.test.js +557 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/case-insensitive-security-token-guard.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cbor.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cert.test.js +843 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba-authreqid-binding.test.js +77 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/ciba.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/clear-site-data.test.js +18 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-erase.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cli.test.js +2790 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster-lease-renewal-ttl.test.js +114 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cluster.test.js +1032 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cms-codec.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +739 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/codepoint-class.test.js +8 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-ai-act.test.js +194 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-lifecycle-proto-key.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/compliance-sanctions.test.js +254 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials.test.js +613 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/cookies.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/cose.test.js +407 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-adversarial.test.js +352 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field-derived-hash.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-field.test.js +575 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-files-parallel.test.js +10 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-hash-stream.test.js +24 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +17 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-spki-pin.test.js +183 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-xwing.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto.test.js +345 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csp-report.test.js +58 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/csv.test.js +48 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/daemon.test.js +459 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dark-patterns.test.js +19 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-query.test.js +1019 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db-raw-residency-gate.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/db.test.js +1621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ddl-change-control.test.js +462 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/dnssec.test.js +323 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dpop-middleware-verify.test.js +562 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/dsr.test.js +901 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/dual-control.test.js +249 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db-migrate.test.js +561 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/external-db.test.js +1214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fal.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/fedcm-dbsc.test.js +46 -20
- package/lib/vendor/blamejs/test/layer-0-primitives/federation-vc-suite.test.js +147 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/fido-mds3.test.js +907 -38
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload-content-safety-skip-audit.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/file-upload.test.js +1135 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/flag.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/forms.test.js +747 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gate-contract.test.js +1855 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/gdpr-ropa.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-archive.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-auth.test.js +117 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-cidr.test.js +151 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-domain.test.js +422 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-filename.test.js +424 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-gate-disposition-coverage.test.js → guard-gate-disposition.test.js} +1 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-graphql.test.js +340 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-html.test.js +29 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-idempotency-key.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-image.test.js +519 -45
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-imap-command.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jmap.test.js +51 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jsonpath.test.js +66 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-jwt.test.js +436 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-managesieve-command.test.js +25 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-markdown.test.js +16 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-mime.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-oauth.test.js +140 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pdf.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-pop3-command.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-regex.test.js +99 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-shell.test.js +76 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{guard-sql-coverage.test.js → guard-sql.test.js} +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-svg.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-template.test.js +71 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-time.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/guard-uuid.test.js +86 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/html-balance.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache-authorization.test.js +347 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client-cache.test.js +908 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-client.test.js +2874 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/http-message-signature.test.js +643 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n-messageformat.test.js +38 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/i18n.test.js +1153 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/idempotency-key.test.js +494 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ip-utils.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jsdoc-example-execution.test.js +177 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-path.test.js +103 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/json-schema.test.js +214 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwk.test.js +41 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/jwt-external.test.js +276 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log-stream.test.js +241 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/log.test.js +106 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-agent.test.js +441 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth.test.js +2136 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bimi.test.js +759 -12
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-bounce.test.js +811 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp-experimental.test.js +401 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-pgp.test.js +751 -8
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto-smime.test.js +864 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-crypto.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dav.test.js +830 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-deploy.test.js +635 -86
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-dkim.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-helo.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-scan.test.js +199 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-send-deliver.test.js +686 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-imap.test.js +778 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-jmap.test.js +1438 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-managesieve.test.js +449 -6
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-mx.test.js +773 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-pop3.test.js +623 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-rate-limit.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-registry.test.js +13 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-server-submission.test.js +877 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-sieve.test.js +326 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store-fts.test.js +55 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-store.test.js +242 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail.test.js +1596 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp.test.js +440 -4
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-snapshot.test.js +89 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics.test.js +1230 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-assetlinks.test.js +113 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-body-parser.test.js +1169 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-bot-disclose.test.js +123 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-cookies.test.js +108 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-gpc.test.js +121 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-headers.test.js +163 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-host-allowlist.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-require-content-type.test.js +127 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{middleware-tus-upload-coverage.test.js → middleware-tus-upload.test.js} +45 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/middleware-web-app-manifest.test.js +132 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mtls-ca-crl-fingerprint-only.test.js +92 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns-resolver.test.js +65 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-dns.test.js +1727 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/network-nts.test.js +699 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy.test.js +1181 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tls.test.js +1876 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/network.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/nonce-store-enforce-replay-nonboolean.test.js +81 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ntp-check.test.js +122 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability-tracing.test.js +378 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/observability.test.js +77 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/oid4vci.test.js +1016 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/openid-federation.test.js +946 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/outbox.test.js +818 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/own-property-membership-guards.test.js +109 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-safe-toml.test.js +543 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/{parsers-safe-yaml-coverage.test.js → parsers-safe-yaml.test.js} +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/parsers-standalone.test.js +73 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/passkey.test.js +649 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/permissions.test.js +524 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pqc-agent-curve.test.js +22 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/pubsub.test.js +295 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue-flow-repeat.test.js +36 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/queue.test.js +83 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/rate-limit-xff-spoofing.test.js +34 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/redact.test.js +1031 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/request-helpers.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/require-bound-key.test.js +401 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/restore-empty-manifest-wipe.test.js +154 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/retention.test.js +600 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-host-path-injection.test.js +231 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router-tls0rtt.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/router.test.js +1277 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-async.test.js +133 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer-linear-scans.test.js +14 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-buffer.test.js +80 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-decompress.test.js +28 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-dns.test.js +245 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ical.test.js +32 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-ini.test.js +376 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-json.test.js +175 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-mime.test.js +518 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-object.test.js +62 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-path.test.js +30 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-schema.test.js +104 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-sql.test.js +142 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-url.test.js +112 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/safe-vcard.test.js +26 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scheduler.test.js +157 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/scim-server.test.js +396 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sd-jwt-vc.test.js +930 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/security-assert.test.js +755 -82
- package/lib/vendor/blamejs/test/layer-0-primitives/self-update.test.js +1053 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-device-binding.test.js +200 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/session-strict-binding-missing-failclosed.test.js +110 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sigv4-bucket-ops.test.js +636 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-offset-without-limit.test.js +94 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sql.test.js +1190 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ssrf-guard.test.js +21 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/static.test.js +850 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage-presigned-url.test.js +146 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/storage.test.js +621 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/subject.test.js +47 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/template.test.js +582 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tenant-quota.test.js +587 -25
- package/lib/vendor/blamejs/test/layer-0-primitives/time.test.js +11 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/tsa.test.js +524 -63
- package/lib/vendor/blamejs/test/layer-0-primitives/uri-template.test.js +27 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault-rotate.test.js +866 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vault.test.js +74 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/vc.test.js +215 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/watcher.test.js +550 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook-dispatcher.test.js +388 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/webhook.test.js +262 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/websocket.test.js +1294 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/ws-client.test.js +629 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/xml-c14n-attr-normalization.test.js +164 -0
- package/lib/vendor/blamejs/test/smoke.js +2 -0
- package/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.16.0.json +0 -44
- package/lib/vendor/blamejs/release-notes/v0.16.1.json +0 -36
- package/lib/vendor/blamejs/release-notes/v0.16.2.json +0 -71
- package/lib/vendor/blamejs/release-notes/v0.16.3.json +0 -71
- package/lib/vendor/blamejs/test/layer-0-primitives/acme-coverage.test.js +0 -441
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-oauth-coverage.test.js +0 -482
- package/lib/vendor/blamejs/test/layer-0-primitives/auth-saml-coverage.test.js +0 -671
- package/lib/vendor/blamejs/test/layer-0-primitives/backup-index-coverage.test.js +0 -690
- package/lib/vendor/blamejs/test/layer-0-primitives/cli-coverage.test.js +0 -238
- package/lib/vendor/blamejs/test/layer-0-primitives/content-credentials-coverage.test.js +0 -319
- package/lib/vendor/blamejs/test/layer-0-primitives/keychain-coverage.test.js +0 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/mail-auth-coverage.test.js +0 -437
- package/lib/vendor/blamejs/test/layer-0-primitives/mcp-coverage.test.js +0 -443
- package/lib/vendor/blamejs/test/layer-0-primitives/metrics-coverage.test.js +0 -395
- package/lib/vendor/blamejs/test/layer-0-primitives/network-smtp-policy-coverage.test.js +0 -565
- package/lib/vendor/blamejs/test/layer-0-primitives/router-coverage.test.js +0 -592
- package/lib/vendor/blamejs/test/layer-0-primitives/sql-coverage.test.js +0 -422
- package/lib/vendor/blamejs/test/layer-0-primitives/test-coverage.test.js +0 -573
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (c) blamejs contributors
|
|
3
|
+
"use strict";
|
|
4
|
+
/**
|
|
5
|
+
* Tests for b.auth.oid4vci — OpenID for Verifiable Credential Issuance
|
|
6
|
+
* 1.0 (issuer side): credential_offer + pre-authorized_code /token
|
|
7
|
+
* exchange + /credential proof-JWT verification + SD-JWT VC minting.
|
|
8
|
+
*
|
|
9
|
+
* Drives the real consumer path (issuer.create → createCredentialOffer
|
|
10
|
+
* → exchangePreAuthorizedCode → issueCredential → metadata). Proof JWTs
|
|
11
|
+
* are minted with real holder keys so the true cryptographic verify path
|
|
12
|
+
* executes; the error / adversarial / defensive branches are reached
|
|
13
|
+
* with hand-crafted proof bytes (real key where a signature must verify,
|
|
14
|
+
* dummy signature where a structural guard throws first). No network —
|
|
15
|
+
* all stores are in-process b.cache instances.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
var helpers = require("../helpers");
|
|
19
|
+
var b = helpers.b;
|
|
20
|
+
var check = helpers.check;
|
|
21
|
+
var nodeCrypto = require("node:crypto");
|
|
22
|
+
var fs = require("node:fs");
|
|
23
|
+
var os = require("node:os");
|
|
24
|
+
var path = require("node:path");
|
|
25
|
+
var AuthError = require("../../lib/framework-error").AuthError;
|
|
26
|
+
|
|
27
|
+
var oid4vci = b.auth.oid4vci;
|
|
28
|
+
var sdJwtVc = b.auth.sdJwtVc;
|
|
29
|
+
|
|
30
|
+
var ISSUER_URL = "https://issuer.example.com";
|
|
31
|
+
|
|
32
|
+
// ---- key + JWK helpers ----
|
|
33
|
+
|
|
34
|
+
function _newEcKey(curve) {
|
|
35
|
+
return nodeCrypto.generateKeyPairSync("ec", { namedCurve: curve || "P-256" });
|
|
36
|
+
}
|
|
37
|
+
function _jwk(publicKey) {
|
|
38
|
+
return publicKey.export({ format: "jwk" });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
var _ecJwk = _jwk(_newEcKey("P-256").publicKey); // reusable public JWK for key-presence-only crafts
|
|
42
|
+
|
|
43
|
+
var _nsCounter = 0;
|
|
44
|
+
function _ns(prefix) { _nsCounter += 1; return prefix + "." + _nsCounter + "." + Date.now(); }
|
|
45
|
+
function _cache() { return b.cache.create({ namespace: _ns("oid4vci.test"), ttlMs: 60000 }); }
|
|
46
|
+
|
|
47
|
+
// A conforming get/set/del store that signals a MISS with `null` rather
|
|
48
|
+
// than `undefined` — the shape a Map / Redis / SQL-row-backed store
|
|
49
|
+
// commonly returns. A framework b.cache returns `undefined` on a miss;
|
|
50
|
+
// this store is a plausible operator implementation the issuer accepts via
|
|
51
|
+
// the documented `cNonceStore` opt. Used to prove the c_nonce replay check
|
|
52
|
+
// fails CLOSED regardless of the store's miss sentinel.
|
|
53
|
+
function _nullMissStore() {
|
|
54
|
+
var m = new Map();
|
|
55
|
+
return {
|
|
56
|
+
get: async function (k) { return m.has(k) ? m.get(k) : null; },
|
|
57
|
+
set: async function (k, v) { m.set(k, v); },
|
|
58
|
+
del: async function (k) { var had = m.has(k); m.delete(k); return had; },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ---- proof-JWT crafting ----
|
|
63
|
+
|
|
64
|
+
function _b64uJson(o) {
|
|
65
|
+
return Buffer.from(JSON.stringify(o), "utf8").toString("base64url");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function _hashByAlg(alg) {
|
|
69
|
+
var m = { ES256: "sha256", ES384: "sha384", ES512: "sha512",
|
|
70
|
+
PS256: "sha256", PS384: "sha384", PS512: "sha512",
|
|
71
|
+
RS256: "sha256", RS384: "sha384", RS512: "sha512", EdDSA: null };
|
|
72
|
+
return m[alg];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function _signProof(header, payload, privateKey, alg) {
|
|
76
|
+
var si = _b64uJson(header) + "." + _b64uJson(payload);
|
|
77
|
+
var input = Buffer.from(si, "ascii");
|
|
78
|
+
var sig;
|
|
79
|
+
if (alg === "EdDSA") {
|
|
80
|
+
sig = nodeCrypto.sign(null, input, privateKey);
|
|
81
|
+
} else if (alg.indexOf("PS") === 0) {
|
|
82
|
+
sig = nodeCrypto.sign(_hashByAlg(alg), input, {
|
|
83
|
+
key: privateKey,
|
|
84
|
+
padding: nodeCrypto.constants.RSA_PKCS1_PSS_PADDING,
|
|
85
|
+
saltLength: nodeCrypto.constants.RSA_PSS_SALTLEN_DIGEST,
|
|
86
|
+
});
|
|
87
|
+
} else if (alg.indexOf("ES") === 0) {
|
|
88
|
+
sig = nodeCrypto.sign(_hashByAlg(alg), input, { key: privateKey, dsaEncoding: "ieee-p1363" });
|
|
89
|
+
} else {
|
|
90
|
+
sig = nodeCrypto.sign(_hashByAlg(alg), input, privateKey);
|
|
91
|
+
}
|
|
92
|
+
return si + "." + sig.toString("base64url");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// A fully valid holder-signed proof JWT. Real signature so _verifyProofJwt's
|
|
96
|
+
// final crypto check succeeds. Overridable so a single field can be
|
|
97
|
+
// corrupted to drive one branch. NOT a mock — mints real signed bytes.
|
|
98
|
+
function _makeProof(o) {
|
|
99
|
+
o = o || {};
|
|
100
|
+
var alg = o.alg || "ES256";
|
|
101
|
+
var kp = o.keyPair || _newEcKey("P-256");
|
|
102
|
+
var nowSec = Math.floor(Date.now() / 1000);
|
|
103
|
+
var header = Object.assign({
|
|
104
|
+
typ: "openid4vci-proof+jwt",
|
|
105
|
+
alg: alg,
|
|
106
|
+
jwk: _jwk(kp.publicKey),
|
|
107
|
+
}, o.headerOverride || {});
|
|
108
|
+
if (o.headerDelete) o.headerDelete.forEach(function (k) { delete header[k]; });
|
|
109
|
+
var payload = Object.assign({
|
|
110
|
+
aud: o.aud !== undefined ? o.aud : ISSUER_URL,
|
|
111
|
+
nonce: o.nonce,
|
|
112
|
+
iat: o.iat !== undefined ? o.iat : nowSec,
|
|
113
|
+
}, o.payloadOverride || {});
|
|
114
|
+
return { jwt: _signProof(header, payload, o.signWith || kp.privateKey, alg), keyPair: kp };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// A structurally-crafted proof with a dummy signature — for the guards that
|
|
118
|
+
// throw BEFORE the crypto verify (typ/alg/crit/key-presence/aud/nonce/iat/
|
|
119
|
+
// x5c-parse/kid-resolve). header/payload extras merge onto minimal defaults.
|
|
120
|
+
function _craftProof(headerExtra, payloadExtra, sig) {
|
|
121
|
+
var nowSec = Math.floor(Date.now() / 1000);
|
|
122
|
+
var header = Object.assign({ typ: "openid4vci-proof+jwt", alg: "ES256" }, headerExtra || {});
|
|
123
|
+
var payload = Object.assign({ aud: ISSUER_URL, iat: nowSec }, payloadExtra || {});
|
|
124
|
+
return _b64uJson(header) + "." + _b64uJson(payload) + "." + (sig || "AAAA");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ---- issuer construction ----
|
|
128
|
+
|
|
129
|
+
function _sdJwtIssuer() {
|
|
130
|
+
var kp = _newEcKey("P-256");
|
|
131
|
+
return sdJwtVc.issuer.create({
|
|
132
|
+
issuerUrl: ISSUER_URL,
|
|
133
|
+
keys: [{ kid: "k1", privateKey: kp.privateKey, algorithm: "ES256" }],
|
|
134
|
+
activeKid: "k1",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function _createOpts(overrides) {
|
|
139
|
+
return Object.assign({
|
|
140
|
+
credentialIssuerUrl: ISSUER_URL,
|
|
141
|
+
credentialEndpoint: ISSUER_URL + "/credential",
|
|
142
|
+
tokenEndpoint: ISSUER_URL + "/token",
|
|
143
|
+
sdJwtIssuer: _sdJwtIssuer(),
|
|
144
|
+
supportedCredentials: {
|
|
145
|
+
"id-card-1": {
|
|
146
|
+
format: "vc+sd-jwt",
|
|
147
|
+
vct: "https://example.com/vct/identity",
|
|
148
|
+
claims: { given_name: {}, family_name: {} },
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
}, overrides || {});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function _create(overrides) {
|
|
155
|
+
return oid4vci.issuer.create(_createOpts(overrides));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function _issued(overrides) {
|
|
159
|
+
var issuer = _create(overrides);
|
|
160
|
+
var offer = await issuer.createCredentialOffer({ subject: "user-9", credentialIds: ["id-card-1"] });
|
|
161
|
+
var tokens = await issuer.exchangePreAuthorizedCode({ preAuthCode: offer.preAuthCode });
|
|
162
|
+
return { issuer: issuer, accessToken: tokens.access_token, cNonce: tokens.c_nonce };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function _expectThrow(label, code, fn) {
|
|
166
|
+
var err = null;
|
|
167
|
+
try { await fn(); } catch (e) { err = e; }
|
|
168
|
+
check(label, err && (code == null || err.code === code));
|
|
169
|
+
return err;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function _expectThrowSync(label, code, fn) {
|
|
173
|
+
var err = null;
|
|
174
|
+
try { fn(); } catch (e) { err = e; }
|
|
175
|
+
check(label, err && (code == null || err.code === code));
|
|
176
|
+
return err;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// convenience: issue against a token-bearing session with a given proof
|
|
180
|
+
function _issue(s, proof, extra) {
|
|
181
|
+
return s.issuer.issueCredential(Object.assign({
|
|
182
|
+
accessToken: s.accessToken,
|
|
183
|
+
credentialIdentifier: "id-card-1",
|
|
184
|
+
proof: proof,
|
|
185
|
+
claims: { given_name: "Alice", family_name: "Smith" },
|
|
186
|
+
}, extra || {}));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ---- happy path: full offer → token → credential flow ----
|
|
190
|
+
|
|
191
|
+
async function testFullFlowHappyPath() {
|
|
192
|
+
var issuer = _create();
|
|
193
|
+
var offer = await issuer.createCredentialOffer({
|
|
194
|
+
subject: "user-42", credentialIds: ["id-card-1"],
|
|
195
|
+
});
|
|
196
|
+
check("offer: deepLink is an openid-credential-offer:// deep link",
|
|
197
|
+
typeof offer.deepLink === "string" &&
|
|
198
|
+
offer.deepLink.indexOf("openid-credential-offer://") === 0);
|
|
199
|
+
check("offer: preAuthCode is a 256-bit hex string",
|
|
200
|
+
typeof offer.preAuthCode === "string" && offer.preAuthCode.length === 64);
|
|
201
|
+
check("offer: offerUri points at the issuer",
|
|
202
|
+
offer.offerUri.indexOf(ISSUER_URL) === 0);
|
|
203
|
+
check("offer: credential_configuration_ids echoed",
|
|
204
|
+
offer.offer.credential_configuration_ids[0] === "id-card-1");
|
|
205
|
+
check("offer: no tx_code when none requested",
|
|
206
|
+
offer.offer.grants["urn:ietf:params:oauth:grant-type:pre-authorized_code"].tx_code === undefined);
|
|
207
|
+
|
|
208
|
+
var tokens = await issuer.exchangePreAuthorizedCode({ preAuthCode: offer.preAuthCode });
|
|
209
|
+
check("token: access_token issued", typeof tokens.access_token === "string" && tokens.access_token.length === 64);
|
|
210
|
+
check("token: token_type Bearer", tokens.token_type === "Bearer");
|
|
211
|
+
check("token: c_nonce issued", typeof tokens.c_nonce === "string" && tokens.c_nonce.length === 32);
|
|
212
|
+
check("token: authorization_details maps credential id",
|
|
213
|
+
tokens.authorization_details[0].credential_configuration_id === "id-card-1" &&
|
|
214
|
+
tokens.authorization_details[0].type === "openid_credential");
|
|
215
|
+
|
|
216
|
+
var proof = _makeProof({ aud: ISSUER_URL, nonce: tokens.c_nonce });
|
|
217
|
+
var cred = await issuer.issueCredential({
|
|
218
|
+
accessToken: tokens.access_token,
|
|
219
|
+
credentialIdentifier: "id-card-1",
|
|
220
|
+
proof: proof.jwt,
|
|
221
|
+
claims: { given_name: "Alice", family_name: "Smith" },
|
|
222
|
+
});
|
|
223
|
+
check("credential: format vc+sd-jwt", cred.format === "vc+sd-jwt");
|
|
224
|
+
check("credential: SD-JWT VC string returned", typeof cred.credential === "string" && cred.credential.indexOf("~") !== -1);
|
|
225
|
+
check("credential: fresh c_nonce rotated", typeof cred.c_nonce === "string" && cred.c_nonce !== tokens.c_nonce);
|
|
226
|
+
|
|
227
|
+
// Single-use default: the access token is consumed → a replay is refused.
|
|
228
|
+
var proof2 = _makeProof({ aud: ISSUER_URL, nonce: cred.c_nonce });
|
|
229
|
+
await _expectThrow("issue: single-use access token refuses replay",
|
|
230
|
+
"auth-oid4vci/invalid-access-token",
|
|
231
|
+
function () { return issuer.issueCredential({
|
|
232
|
+
accessToken: tokens.access_token, credentialIdentifier: "id-card-1",
|
|
233
|
+
proof: proof2.jwt, claims: { given_name: "Bob" } }); });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async function testMetadataDocument() {
|
|
237
|
+
var issuer = _create();
|
|
238
|
+
var md = issuer.metadata();
|
|
239
|
+
check("metadata: credential_issuer", md.credential_issuer === ISSUER_URL);
|
|
240
|
+
check("metadata: credential_endpoint", md.credential_endpoint === ISSUER_URL + "/credential");
|
|
241
|
+
check("metadata: token_endpoint", md.token_endpoint === ISSUER_URL + "/token");
|
|
242
|
+
check("metadata: authorization_servers defaults to [issuer]",
|
|
243
|
+
Array.isArray(md.authorization_servers) && md.authorization_servers[0] === ISSUER_URL);
|
|
244
|
+
var cfg = md.credential_configurations_supported["id-card-1"];
|
|
245
|
+
check("metadata: configuration format", cfg.format === "vc+sd-jwt");
|
|
246
|
+
check("metadata: default cryptographic_binding_methods jwk",
|
|
247
|
+
cfg.cryptographic_binding_methods_supported[0] === "jwk");
|
|
248
|
+
check("metadata: default signing alg ES256",
|
|
249
|
+
cfg.credential_signing_alg_values_supported[0] === "ES256");
|
|
250
|
+
check("metadata: proof_types_supported carries proofAlgs",
|
|
251
|
+
cfg.proof_types_supported.jwt.proof_signing_alg_values_supported.indexOf("ES256") !== -1);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async function testMetadataCustomFields() {
|
|
255
|
+
var issuer = _create({
|
|
256
|
+
authorizationServers: ["https://as.example.com"],
|
|
257
|
+
proofAlgorithms: ["ES256", "EdDSA"],
|
|
258
|
+
supportedCredentials: {
|
|
259
|
+
"vip": {
|
|
260
|
+
format: "dc+sd-jwt",
|
|
261
|
+
vct: "https://example.com/vct/vip",
|
|
262
|
+
claims: { tier: {} },
|
|
263
|
+
cryptographic_binding_methods_supported: ["cose_key"],
|
|
264
|
+
credential_signing_alg_values_supported: ["EdDSA"],
|
|
265
|
+
display: [{ name: "VIP Card", locale: "en-US" }],
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
var md = issuer.metadata();
|
|
270
|
+
check("metadata: custom authorization_servers reflected",
|
|
271
|
+
md.authorization_servers[0] === "https://as.example.com");
|
|
272
|
+
var cfg = md.credential_configurations_supported["vip"];
|
|
273
|
+
check("metadata: custom binding methods reflected", cfg.cryptographic_binding_methods_supported[0] === "cose_key");
|
|
274
|
+
check("metadata: custom signing algs reflected", cfg.credential_signing_alg_values_supported[0] === "EdDSA");
|
|
275
|
+
check("metadata: display block passed through", cfg.display[0].name === "VIP Card");
|
|
276
|
+
check("metadata: dc+sd-jwt format accepted", cfg.format === "dc+sd-jwt");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ---- create(): config-time validation ----
|
|
280
|
+
|
|
281
|
+
function testCreateRequireObject() {
|
|
282
|
+
_expectThrowSync("create: null opts rejected", null, function () { oid4vci.issuer.create(null); });
|
|
283
|
+
_expectThrowSync("create: string opts rejected", null, function () { oid4vci.issuer.create("nope"); });
|
|
284
|
+
_expectThrowSync("create: number opts rejected", null, function () { oid4vci.issuer.create(7); });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function testCreateMissingRequiredStrings() {
|
|
288
|
+
_expectThrowSync("create: missing credentialIssuerUrl", "auth-oid4vci/no-issuer-url",
|
|
289
|
+
function () { oid4vci.issuer.create(_createOpts({ credentialIssuerUrl: 123 })); });
|
|
290
|
+
_expectThrowSync("create: missing credentialEndpoint", "auth-oid4vci/no-credential-endpoint",
|
|
291
|
+
function () { oid4vci.issuer.create(_createOpts({ credentialEndpoint: 123 })); });
|
|
292
|
+
_expectThrowSync("create: missing tokenEndpoint", "auth-oid4vci/no-token-endpoint",
|
|
293
|
+
function () { oid4vci.issuer.create(_createOpts({ tokenEndpoint: 123 })); });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function testCreateNoSdJwtIssuer() {
|
|
297
|
+
_expectThrowSync("create: sdJwtIssuer without issue() rejected", "auth-oid4vci/no-sd-jwt-issuer",
|
|
298
|
+
function () { oid4vci.issuer.create(_createOpts({ sdJwtIssuer: { notIssue: 1 } })); });
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function testCreateNoSupportedCredentials() {
|
|
302
|
+
_expectThrowSync("create: empty supportedCredentials rejected", "auth-oid4vci/no-supported-credentials",
|
|
303
|
+
function () { oid4vci.issuer.create(_createOpts({ supportedCredentials: {} })); });
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function testCreateBadCredentialSpec() {
|
|
307
|
+
_expectThrowSync("create: non-object credential spec rejected", "auth-oid4vci/bad-credential-spec",
|
|
308
|
+
function () { oid4vci.issuer.create(_createOpts({ supportedCredentials: { x: "not-an-object" } })); });
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function testCreateUnsupportedFormat() {
|
|
312
|
+
_expectThrowSync("create: unsupported credential format rejected", "auth-oid4vci/unsupported-format",
|
|
313
|
+
function () { oid4vci.issuer.create(_createOpts({
|
|
314
|
+
supportedCredentials: { x: { format: "image/jpeg", vct: "https://y" } } })); });
|
|
315
|
+
_expectThrowSync("create: missing credential format rejected", "auth-oid4vci/unsupported-format",
|
|
316
|
+
function () { oid4vci.issuer.create(_createOpts({
|
|
317
|
+
supportedCredentials: { x: { vct: "https://y" } } })); });
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function testCreateNoVct() {
|
|
321
|
+
_expectThrowSync("create: missing vct rejected", "auth-oid4vci/no-vct",
|
|
322
|
+
function () { oid4vci.issuer.create(_createOpts({
|
|
323
|
+
supportedCredentials: { x: { format: "vc+sd-jwt" } } })); });
|
|
324
|
+
_expectThrowSync("create: empty vct rejected", "auth-oid4vci/no-vct",
|
|
325
|
+
function () { oid4vci.issuer.create(_createOpts({
|
|
326
|
+
supportedCredentials: { x: { format: "vc+sd-jwt", vct: "" } } })); });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function testCreateBadResolveKidType() {
|
|
330
|
+
_expectThrowSync("create: non-function resolveKid rejected at config time",
|
|
331
|
+
"auth-oid4vci/bad-resolve-kid",
|
|
332
|
+
function () { oid4vci.issuer.create(_createOpts({ resolveKid: "not-a-function" })); });
|
|
333
|
+
_expectThrowSync("create: non-function validateX5c rejected at config time",
|
|
334
|
+
"auth-oid4vci/bad-validate-x5c",
|
|
335
|
+
function () { oid4vci.issuer.create(_createOpts({ validateX5c: 42 })); });
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ---- createCredentialOffer(): defensive branches ----
|
|
339
|
+
|
|
340
|
+
async function testOfferBadSubject() {
|
|
341
|
+
var issuer = _create();
|
|
342
|
+
await _expectThrow("offer: missing subject rejected", "auth-oid4vci/no-subject",
|
|
343
|
+
function () { return issuer.createCredentialOffer({ credentialIds: ["id-card-1"] }); });
|
|
344
|
+
await _expectThrow("offer: empty subject rejected", "auth-oid4vci/no-subject",
|
|
345
|
+
function () { return issuer.createCredentialOffer({ subject: "", credentialIds: ["id-card-1"] }); });
|
|
346
|
+
await _expectThrow("offer: no opts at all rejected", "auth-oid4vci/no-subject",
|
|
347
|
+
function () { return issuer.createCredentialOffer(); });
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async function testOfferBadCredentialIds() {
|
|
351
|
+
var issuer = _create();
|
|
352
|
+
await _expectThrow("offer: missing credentialIds rejected", "auth-oid4vci/no-credential-ids",
|
|
353
|
+
function () { return issuer.createCredentialOffer({ subject: "u" }); });
|
|
354
|
+
await _expectThrow("offer: empty credentialIds rejected", "auth-oid4vci/no-credential-ids",
|
|
355
|
+
function () { return issuer.createCredentialOffer({ subject: "u", credentialIds: [] }); });
|
|
356
|
+
await _expectThrow("offer: non-array credentialIds rejected", "auth-oid4vci/no-credential-ids",
|
|
357
|
+
function () { return issuer.createCredentialOffer({ subject: "u", credentialIds: "id-card-1" }); });
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
async function testOfferUnknownCredentialId() {
|
|
361
|
+
var issuer = _create();
|
|
362
|
+
await _expectThrow("offer: unknown credentialId rejected", "auth-oid4vci/unknown-credential-id",
|
|
363
|
+
function () { return issuer.createCredentialOffer({ subject: "u", credentialIds: ["ghost"] }); });
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function testOfferBadTxCode() {
|
|
367
|
+
var issuer = _create();
|
|
368
|
+
await _expectThrow("offer: string txCode rejected", "auth-oid4vci/bad-tx-code",
|
|
369
|
+
function () { return issuer.createCredentialOffer({ subject: "u", credentialIds: ["id-card-1"], txCode: "1234" }); });
|
|
370
|
+
await _expectThrow("offer: txCode without string value rejected", "auth-oid4vci/bad-tx-code",
|
|
371
|
+
function () { return issuer.createCredentialOffer({ subject: "u", credentialIds: ["id-card-1"], txCode: { length: 4 } }); });
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
async function testOfferTxCodeDefaults() {
|
|
375
|
+
var issuer = _create();
|
|
376
|
+
var offer = await issuer.createCredentialOffer({
|
|
377
|
+
subject: "u", credentialIds: ["id-card-1"], txCode: { value: "1234" },
|
|
378
|
+
});
|
|
379
|
+
var tx = offer.offer.grants["urn:ietf:params:oauth:grant-type:pre-authorized_code"].tx_code;
|
|
380
|
+
check("offer: tx_code default length is 4", tx.length === 4);
|
|
381
|
+
check("offer: tx_code default input_mode numeric", tx.input_mode === "numeric");
|
|
382
|
+
check("offer: tx_code description omitted when absent", tx.description === undefined);
|
|
383
|
+
check("offer: hasTxCode reflected in return", offer.offer.grants["urn:ietf:params:oauth:grant-type:pre-authorized_code"]["pre-authorized_code"] === offer.preAuthCode);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function testOfferTxCodeExplicit() {
|
|
387
|
+
var issuer = _create();
|
|
388
|
+
var offer = await issuer.createCredentialOffer({
|
|
389
|
+
subject: "u", credentialIds: ["id-card-1"],
|
|
390
|
+
txCode: { value: "998877", length: 6, input_mode: "text", description: "Enter the PIN from the kiosk" },
|
|
391
|
+
});
|
|
392
|
+
var tx = offer.offer.grants["urn:ietf:params:oauth:grant-type:pre-authorized_code"].tx_code;
|
|
393
|
+
check("offer: tx_code explicit length honored", tx.length === 6);
|
|
394
|
+
check("offer: tx_code explicit input_mode honored", tx.input_mode === "text");
|
|
395
|
+
check("offer: tx_code description honored", tx.description === "Enter the PIN from the kiosk");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// ---- exchangePreAuthorizedCode(): defensive branches ----
|
|
399
|
+
|
|
400
|
+
async function testExchangeMissingCode() {
|
|
401
|
+
var issuer = _create();
|
|
402
|
+
await _expectThrow("exchange: missing pre-auth code rejected", "auth-oid4vci/missing-pre-auth-code",
|
|
403
|
+
function () { return issuer.exchangePreAuthorizedCode({}); });
|
|
404
|
+
await _expectThrow("exchange: empty pre-auth code rejected", "auth-oid4vci/missing-pre-auth-code",
|
|
405
|
+
function () { return issuer.exchangePreAuthorizedCode({ preAuthCode: "" }); });
|
|
406
|
+
await _expectThrow("exchange: no opts rejected", "auth-oid4vci/missing-pre-auth-code",
|
|
407
|
+
function () { return issuer.exchangePreAuthorizedCode(); });
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function testExchangeUnknownCode() {
|
|
411
|
+
var issuer = _create();
|
|
412
|
+
await _expectThrow("exchange: unknown pre-auth code rejected", "auth-oid4vci/invalid-pre-auth-code",
|
|
413
|
+
function () { return issuer.exchangePreAuthorizedCode({ preAuthCode: "deadbeef-not-issued" }); });
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async function testExchangeTxCodeRequired() {
|
|
417
|
+
var issuer = _create();
|
|
418
|
+
var offer = await issuer.createCredentialOffer({
|
|
419
|
+
subject: "u", credentialIds: ["id-card-1"], txCode: { value: "1234" },
|
|
420
|
+
});
|
|
421
|
+
await _expectThrow("exchange: tx_code required but omitted", "auth-oid4vci/missing-tx-code",
|
|
422
|
+
function () { return issuer.exchangePreAuthorizedCode({ preAuthCode: offer.preAuthCode }); });
|
|
423
|
+
await _expectThrow("exchange: tx_code required but empty", "auth-oid4vci/missing-tx-code",
|
|
424
|
+
function () { return issuer.exchangePreAuthorizedCode({ preAuthCode: offer.preAuthCode, txCode: "" }); });
|
|
425
|
+
// Code is NOT consumed on a missing tx_code — a subsequent correct call still works.
|
|
426
|
+
var ok = await issuer.exchangePreAuthorizedCode({ preAuthCode: offer.preAuthCode, txCode: "1234" });
|
|
427
|
+
check("exchange: tx_code path not consumed on prior missing-tx-code", typeof ok.access_token === "string");
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
async function testExchangeTxCodeMismatch() {
|
|
431
|
+
var issuer = _create();
|
|
432
|
+
var offer = await issuer.createCredentialOffer({
|
|
433
|
+
subject: "u", credentialIds: ["id-card-1"], txCode: { value: "1234" },
|
|
434
|
+
});
|
|
435
|
+
await _expectThrow("exchange: wrong tx_code rejected", "auth-oid4vci/tx-code-mismatch",
|
|
436
|
+
function () { return issuer.exchangePreAuthorizedCode({ preAuthCode: offer.preAuthCode, txCode: "9999" }); });
|
|
437
|
+
// Not consumed on mismatch: the correct tx_code still redeems it.
|
|
438
|
+
var ok = await issuer.exchangePreAuthorizedCode({ preAuthCode: offer.preAuthCode, txCode: "1234" });
|
|
439
|
+
check("exchange: correct tx_code redeems after a mismatch", typeof ok.access_token === "string");
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function testExchangeAlreadyRedeemedRace() {
|
|
443
|
+
// The atomic single-use delete: the loser of a concurrent redemption sees
|
|
444
|
+
// del() return false. Simulated deterministically by overriding the store's
|
|
445
|
+
// del to return false while the entry is still gettable.
|
|
446
|
+
var codeStore = _cache();
|
|
447
|
+
var issuer = _create({ codeStore: codeStore });
|
|
448
|
+
var offer = await issuer.createCredentialOffer({ subject: "u", credentialIds: ["id-card-1"] });
|
|
449
|
+
codeStore.del = function () { return Promise.resolve(false); };
|
|
450
|
+
await _expectThrow("exchange: lost single-use delete race refused", "auth-oid4vci/invalid-pre-auth-code",
|
|
451
|
+
function () { return issuer.exchangePreAuthorizedCode({ preAuthCode: offer.preAuthCode }); });
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ---- issueCredential(): non-proof defensive branches ----
|
|
455
|
+
|
|
456
|
+
async function testIssueMissingAccessToken() {
|
|
457
|
+
var issuer = _create();
|
|
458
|
+
await _expectThrow("issue: missing access token rejected", "auth-oid4vci/missing-access-token",
|
|
459
|
+
function () { return issuer.issueCredential({ credentialIdentifier: "id-card-1", proof: "x", claims: {} }); });
|
|
460
|
+
await _expectThrow("issue: empty access token rejected", "auth-oid4vci/missing-access-token",
|
|
461
|
+
function () { return issuer.issueCredential({ accessToken: "", proof: "x", claims: {} }); });
|
|
462
|
+
await _expectThrow("issue: no opts rejected", "auth-oid4vci/missing-access-token",
|
|
463
|
+
function () { return issuer.issueCredential(); });
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
async function testIssueInvalidAccessToken() {
|
|
467
|
+
var issuer = _create();
|
|
468
|
+
await _expectThrow("issue: unknown access token rejected", "auth-oid4vci/invalid-access-token",
|
|
469
|
+
function () { return issuer.issueCredential({ accessToken: "never-issued", credentialIdentifier: "id-card-1", proof: "x", claims: {} }); });
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function testIssueWrongCredentialIdentifier() {
|
|
473
|
+
var s = await _issued();
|
|
474
|
+
await _expectThrow("issue: credentialIdentifier outside token's set rejected",
|
|
475
|
+
"auth-oid4vci/wrong-credential-identifier",
|
|
476
|
+
function () { return s.issuer.issueCredential({ accessToken: s.accessToken, credentialIdentifier: "other-id", proof: "x", claims: {} }); });
|
|
477
|
+
await _expectThrow("issue: non-string credentialIdentifier rejected",
|
|
478
|
+
"auth-oid4vci/wrong-credential-identifier",
|
|
479
|
+
function () { return s.issuer.issueCredential({ accessToken: s.accessToken, proof: "x", claims: {} }); });
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async function testIssueUnknownCredentialId() {
|
|
483
|
+
// A record whose credentialIds carries an id NOT in supportedCredentials
|
|
484
|
+
// (defensive — createCredentialOffer forbids it). Pre-seed a store to reach
|
|
485
|
+
// the guard directly on the real issueCredential path.
|
|
486
|
+
var atStore = _cache();
|
|
487
|
+
var issuer = _create({ accessTokenStore: atStore });
|
|
488
|
+
await atStore.set("seeded-token", { subject: "u", credentialIds: ["ghost-id"] });
|
|
489
|
+
await _expectThrow("issue: credentialId in token but not configured rejected",
|
|
490
|
+
"auth-oid4vci/unknown-credential-id",
|
|
491
|
+
function () { return issuer.issueCredential({ accessToken: "seeded-token", credentialIdentifier: "ghost-id", proof: "x.y.z", claims: {} }); });
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function testIssueNoClaims() {
|
|
495
|
+
var s = await _issued();
|
|
496
|
+
var proof = _makeProof({ aud: ISSUER_URL, nonce: s.cNonce });
|
|
497
|
+
await _expectThrow("issue: missing claims rejected", "auth-oid4vci/no-claims",
|
|
498
|
+
function () { return s.issuer.issueCredential({ accessToken: s.accessToken, credentialIdentifier: "id-card-1", proof: proof.jwt }); });
|
|
499
|
+
var s2 = await _issued();
|
|
500
|
+
var proof2 = _makeProof({ aud: ISSUER_URL, nonce: s2.cNonce });
|
|
501
|
+
await _expectThrow("issue: non-object claims rejected", "auth-oid4vci/no-claims",
|
|
502
|
+
function () { return s2.issuer.issueCredential({ accessToken: s2.accessToken, credentialIdentifier: "id-card-1", proof: proof2.jwt, claims: "alice" }); });
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
async function testIssueAccessTokenConsumedRace() {
|
|
506
|
+
// The single-use access-token claim is an atomic delete; the loser sees
|
|
507
|
+
// del() return false → access-token-consumed. Simulated by overriding the
|
|
508
|
+
// access-token store's del to return false after a real proof verifies.
|
|
509
|
+
var atStore = _cache();
|
|
510
|
+
var s = await _issued({ accessTokenStore: atStore });
|
|
511
|
+
var proof = _makeProof({ aud: ISSUER_URL, nonce: s.cNonce });
|
|
512
|
+
atStore.del = function () { return Promise.resolve(false); };
|
|
513
|
+
await _expectThrow("issue: lost single-use token claim refused", "auth-oid4vci/access-token-consumed",
|
|
514
|
+
function () { return _issue(s, proof.jwt); });
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
async function testIssueMintThrowsRestoresToken() {
|
|
518
|
+
// The operator's sdJwtIssuer throws AFTER the single-use token is claimed;
|
|
519
|
+
// the token must be restored so a retry can succeed (the claim only guards
|
|
520
|
+
// against a concurrent double-mint, not against a failed mint).
|
|
521
|
+
var throwingIssuer = {
|
|
522
|
+
issue: function () { return Promise.reject(new Error("signer KMS outage")); },
|
|
523
|
+
};
|
|
524
|
+
var s = await _issued({ sdJwtIssuer: throwingIssuer });
|
|
525
|
+
var proof = _makeProof({ aud: ISSUER_URL, nonce: s.cNonce });
|
|
526
|
+
var err1 = await _expectThrow("issue: mint failure propagates the operator error", null,
|
|
527
|
+
function () { return _issue(s, proof.jwt); });
|
|
528
|
+
check("issue: propagated error is the signer outage", err1 && /KMS outage/.test(err1.message));
|
|
529
|
+
// Token was restored: a second attempt reaches the mint again (throws the
|
|
530
|
+
// same operator error) rather than access-token-consumed.
|
|
531
|
+
var proof2 = _makeProof({ aud: ISSUER_URL, nonce: s.cNonce });
|
|
532
|
+
var err2 = await _expectThrow("issue: token restored after mint failure (retryable)", null,
|
|
533
|
+
function () { return _issue(s, proof2.jwt); });
|
|
534
|
+
check("issue: retry hits the signer again, not a consumed-token refusal",
|
|
535
|
+
err2 && err2.code !== "auth-oid4vci/access-token-consumed");
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
async function testIssueSingleUseOptOut() {
|
|
539
|
+
var s = await _issued({ accessTokenSingleUse: false });
|
|
540
|
+
var proof1 = _makeProof({ aud: ISSUER_URL, nonce: s.cNonce });
|
|
541
|
+
var cred1 = await _issue(s, proof1.jwt);
|
|
542
|
+
check("issue: opt-out first issuance succeeds", cred1.format === "vc+sd-jwt");
|
|
543
|
+
// Token NOT consumed — a second issuance with the rotated c_nonce works.
|
|
544
|
+
var proof2 = _makeProof({ aud: ISSUER_URL, nonce: cred1.c_nonce });
|
|
545
|
+
var cred2 = await _issue(s, proof2.jwt);
|
|
546
|
+
check("issue: opt-out access token is reusable", cred2.format === "vc+sd-jwt");
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
async function testIssueCustomDisclosureAndTtl() {
|
|
550
|
+
var s = await _issued();
|
|
551
|
+
var proof = _makeProof({ aud: ISSUER_URL, nonce: s.cNonce });
|
|
552
|
+
var cred = await s.issuer.issueCredential({
|
|
553
|
+
accessToken: s.accessToken, credentialIdentifier: "id-card-1", proof: proof.jwt,
|
|
554
|
+
claims: { given_name: "Alice", family_name: "Smith" },
|
|
555
|
+
selectivelyDisclosed: ["given_name"],
|
|
556
|
+
ttlMs: 3600000,
|
|
557
|
+
});
|
|
558
|
+
check("issue: explicit selectivelyDisclosed + ttlMs issues a credential", cred.format === "vc+sd-jwt");
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async function testIssueProofMaxAgeCustom() {
|
|
562
|
+
// proofMaxAgeMs widens the iat-too-old window; a proof older than the
|
|
563
|
+
// 10-minute default still verifies under a 20-minute policy.
|
|
564
|
+
var s = await _issued({ proofMaxAgeMs: 1200000 }); // 20 minutes in ms
|
|
565
|
+
var nowSec = Math.floor(Date.now() / 1000);
|
|
566
|
+
var proof = _makeProof({ aud: ISSUER_URL, nonce: s.cNonce, iat: nowSec - 900 }); // 15 min ago
|
|
567
|
+
var cred = await _issue(s, proof.jwt);
|
|
568
|
+
check("issue: proofMaxAgeMs override accepts an aged-but-in-window proof", cred.format === "vc+sd-jwt");
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// ---- _verifyProofJwt: structural / payload branches ----
|
|
572
|
+
|
|
573
|
+
async function testProofEmptyOrTooLong() {
|
|
574
|
+
var s = await _issued();
|
|
575
|
+
await _expectThrow("proof: empty string rejected", "auth-oid4vci/bad-proof",
|
|
576
|
+
function () { return _issue(s, ""); });
|
|
577
|
+
await _expectThrow("proof: non-string rejected", "auth-oid4vci/bad-proof",
|
|
578
|
+
function () { return _issue(s, 12345); });
|
|
579
|
+
var s2 = await _issued();
|
|
580
|
+
await _expectThrow("proof: over-size proof rejected", "auth-oid4vci/bad-proof",
|
|
581
|
+
function () { return _issue(s2, "x".repeat(40000)); });
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async function testProofNotThreeParts() {
|
|
585
|
+
var s = await _issued();
|
|
586
|
+
await _expectThrow("proof: not 3 dot-parts rejected", "auth-oid4vci/malformed-proof",
|
|
587
|
+
function () { return _issue(s, "only.two"); });
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
async function testProofBadDecode() {
|
|
591
|
+
var s = await _issued();
|
|
592
|
+
var proof = Buffer.from("not-json-at-all", "utf8").toString("base64url") + "." + _b64uJson({}) + ".AAAA";
|
|
593
|
+
await _expectThrow("proof: undecodable header rejected", "auth-oid4vci/bad-proof-decode",
|
|
594
|
+
function () { return _issue(s, proof); });
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async function testProofWrongTyp() {
|
|
598
|
+
var s = await _issued();
|
|
599
|
+
var proof = _craftProof({ typ: "JWT", jwk: _ecJwk }, { nonce: s.cNonce });
|
|
600
|
+
await _expectThrow("proof: wrong typ rejected", "auth-oid4vci/wrong-proof-typ",
|
|
601
|
+
function () { return _issue(s, proof); });
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
async function testProofUnsupportedAlgAllowlist() {
|
|
605
|
+
var s = await _issued();
|
|
606
|
+
var proof = _craftProof({ alg: "HS256", jwk: _ecJwk }, { nonce: s.cNonce });
|
|
607
|
+
await _expectThrow("proof: alg outside issuer set refused before key lookup",
|
|
608
|
+
"auth-oid4vci/unsupported-proof-alg",
|
|
609
|
+
function () { return _issue(s, proof); });
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
async function testProofCritRefused() {
|
|
613
|
+
var s = await _issued();
|
|
614
|
+
var withCrit = _craftProof({ jwk: _ecJwk, crit: ["unknown-ext"] }, { nonce: s.cNonce });
|
|
615
|
+
await _expectThrow("proof: non-empty crit refused", "auth-oid4vci/unknown-crit",
|
|
616
|
+
function () { return _issue(s, withCrit); });
|
|
617
|
+
var badCrit = _craftProof({ jwk: _ecJwk, crit: "not-an-array" }, { nonce: s.cNonce });
|
|
618
|
+
await _expectThrow("proof: non-array crit refused", "auth-oid4vci/unknown-crit",
|
|
619
|
+
function () { return _issue(s, badCrit); });
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
async function testProofNoKey() {
|
|
623
|
+
var s = await _issued();
|
|
624
|
+
var proof = _craftProof({}, { nonce: s.cNonce }); // no jwk / kid / x5c
|
|
625
|
+
await _expectThrow("proof: no holder-key binding rejected", "auth-oid4vci/no-key-in-proof",
|
|
626
|
+
function () { return _issue(s, proof); });
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
async function testProofWrongAud() {
|
|
630
|
+
var s = await _issued();
|
|
631
|
+
var proof = _craftProof({ jwk: _ecJwk }, { aud: "https://evil.example", nonce: s.cNonce });
|
|
632
|
+
await _expectThrow("proof: wrong aud rejected", "auth-oid4vci/wrong-proof-aud",
|
|
633
|
+
function () { return _issue(s, proof); });
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
async function testProofCNonceExpired() {
|
|
637
|
+
var cNonceStore = _cache();
|
|
638
|
+
var s = await _issued({ cNonceStore: cNonceStore });
|
|
639
|
+
await cNonceStore.del(s.accessToken); // c_nonce TTL elapsed before /credential
|
|
640
|
+
var proof = _craftProof({ jwk: _ecJwk }, { aud: ISSUER_URL, nonce: "whatever" });
|
|
641
|
+
await _expectThrow("proof: expected c_nonce missing/expired rejected", "auth-oid4vci/c-nonce-expired",
|
|
642
|
+
function () { return _issue(s, proof); });
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
async function testProofWrongNonce() {
|
|
646
|
+
var s = await _issued();
|
|
647
|
+
var wrong = _craftProof({ jwk: _ecJwk }, { aud: ISSUER_URL, nonce: "not-the-nonce" });
|
|
648
|
+
await _expectThrow("proof: nonce mismatch rejected (replay defense)", "auth-oid4vci/wrong-proof-nonce",
|
|
649
|
+
function () { return _issue(s, wrong); });
|
|
650
|
+
var s2 = await _issued();
|
|
651
|
+
var numeric = _craftProof({ jwk: _ecJwk }, { aud: ISSUER_URL, nonce: 12345 });
|
|
652
|
+
await _expectThrow("proof: non-string nonce rejected", "auth-oid4vci/wrong-proof-nonce",
|
|
653
|
+
function () { return _issue(s2, numeric); });
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// A c_nonce store that signals a miss with `null` (not `undefined`) MUST
|
|
657
|
+
// still fail closed. Before the fix, a null store-miss fell through the
|
|
658
|
+
// nonce compare entirely: a forged proof carrying ANY nonce was accepted
|
|
659
|
+
// and a credential minted bound to the attacker's key. Batch mode
|
|
660
|
+
// (accessTokenSingleUse: false) keeps the access token valid past the
|
|
661
|
+
// c_nonce's shorter TTL, so an attacker who lets the c_nonce expire can
|
|
662
|
+
// reach /credential with a live token and no valid nonce.
|
|
663
|
+
async function testProofCNonceNullStoreMissFailsClosed() {
|
|
664
|
+
var cNonceStore = _nullMissStore();
|
|
665
|
+
var issuer = _create({ cNonceStore: cNonceStore, accessTokenSingleUse: false });
|
|
666
|
+
var offer = await issuer.createCredentialOffer({ subject: "user-9", credentialIds: ["id-card-1"] });
|
|
667
|
+
var tokens = await issuer.exchangePreAuthorizedCode({ preAuthCode: offer.preAuthCode });
|
|
668
|
+
|
|
669
|
+
// A valid c_nonce present → issuance succeeds (the store's null-miss
|
|
670
|
+
// sentinel must not break the happy path).
|
|
671
|
+
var good = _makeProof({ aud: ISSUER_URL, nonce: tokens.c_nonce });
|
|
672
|
+
var cred = await issuer.issueCredential({
|
|
673
|
+
accessToken: tokens.access_token, credentialIdentifier: "id-card-1",
|
|
674
|
+
proof: good.jwt, claims: { given_name: "Alice" },
|
|
675
|
+
});
|
|
676
|
+
check("null-miss store: valid c_nonce still issues", cred.format === "vc+sd-jwt");
|
|
677
|
+
|
|
678
|
+
// Simulate the c_nonce expiring while the access token is still live —
|
|
679
|
+
// the store now returns null on get(). A forged proof with a garbage
|
|
680
|
+
// nonce (never issued) MUST be refused, not accepted.
|
|
681
|
+
await cNonceStore.del(tokens.access_token);
|
|
682
|
+
check("null-miss store: get() returns null on miss",
|
|
683
|
+
(await cNonceStore.get(tokens.access_token)) === null);
|
|
684
|
+
var forged = _makeProof({ aud: ISSUER_URL, nonce: "attacker-garbage-nonce-not-issued" });
|
|
685
|
+
await _expectThrow("null-miss store: absent c_nonce refuses forged proof (fail-closed)",
|
|
686
|
+
"auth-oid4vci/c-nonce-expired",
|
|
687
|
+
function () {
|
|
688
|
+
return issuer.issueCredential({
|
|
689
|
+
accessToken: tokens.access_token, credentialIdentifier: "id-card-1",
|
|
690
|
+
proof: forged.jwt, claims: { given_name: "Mallory" },
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
async function testProofNoIat() {
|
|
696
|
+
var s = await _issued();
|
|
697
|
+
var proof = _craftProof({ jwk: _ecJwk }, { aud: ISSUER_URL, nonce: s.cNonce, iat: undefined });
|
|
698
|
+
await _expectThrow("proof: missing iat rejected", "auth-oid4vci/no-proof-iat",
|
|
699
|
+
function () { return _issue(s, proof); });
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
async function testProofIatFuture() {
|
|
703
|
+
var s = await _issued();
|
|
704
|
+
var nowSec = Math.floor(Date.now() / 1000);
|
|
705
|
+
var proof = _craftProof({ jwk: _ecJwk }, { aud: ISSUER_URL, nonce: s.cNonce, iat: nowSec + 3600 });
|
|
706
|
+
await _expectThrow("proof: future iat rejected", "auth-oid4vci/proof-iat-future",
|
|
707
|
+
function () { return _issue(s, proof); });
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
async function testProofIatTooOld() {
|
|
711
|
+
var s = await _issued();
|
|
712
|
+
var nowSec = Math.floor(Date.now() / 1000);
|
|
713
|
+
var proof = _craftProof({ jwk: _ecJwk }, { aud: ISSUER_URL, nonce: s.cNonce, iat: nowSec - 3600 });
|
|
714
|
+
await _expectThrow("proof: stale iat rejected", "auth-oid4vci/proof-iat-too-old",
|
|
715
|
+
function () { return _issue(s, proof); });
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// ---- _verifyProofJwt: kid resolution ----
|
|
719
|
+
|
|
720
|
+
async function testKidNoResolver() {
|
|
721
|
+
var s = await _issued(); // no resolveKid configured
|
|
722
|
+
var proof = _craftProof({ kid: "holder-kid-1" }, { nonce: s.cNonce });
|
|
723
|
+
await _expectThrow("proof: kid without inline jwk and no resolver refused",
|
|
724
|
+
"auth-oid4vci/kid-resolver-not-supported",
|
|
725
|
+
function () { return _issue(s, proof); });
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
async function testKidResolverThrows() {
|
|
729
|
+
var s = await _issued({ resolveKid: function () { throw new Error("kid DB down"); } });
|
|
730
|
+
var proof = _craftProof({ kid: "holder-kid-1" }, { nonce: s.cNonce });
|
|
731
|
+
var err = await _expectThrow("proof: resolveKid throw wrapped as typed refusal",
|
|
732
|
+
"auth-oid4vci/kid-resolver-failed",
|
|
733
|
+
function () { return _issue(s, proof); });
|
|
734
|
+
check("proof: resolver error message surfaced for operator debugging",
|
|
735
|
+
err && /kid DB down/.test(err.message));
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
async function testKidResolverUnresolved() {
|
|
739
|
+
var s = await _issued({ resolveKid: function () { return null; } });
|
|
740
|
+
var proof = _craftProof({ kid: "holder-kid-1" }, { nonce: s.cNonce });
|
|
741
|
+
await _expectThrow("proof: resolveKid returning no key refused", "auth-oid4vci/kid-unresolved",
|
|
742
|
+
function () { return _issue(s, proof); });
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
async function testKidResolverBadShape() {
|
|
746
|
+
var s = await _issued({ resolveKid: function () { return { not: "a-key" }; } });
|
|
747
|
+
var proof = _craftProof({ kid: "holder-kid-1" }, { nonce: s.cNonce });
|
|
748
|
+
await _expectThrow("proof: resolveKid returning a non-key object refused",
|
|
749
|
+
"auth-oid4vci/bad-resolved-key",
|
|
750
|
+
function () { return _issue(s, proof); });
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
async function testKidResolverKeyObject() {
|
|
754
|
+
var holder = _newEcKey("P-256");
|
|
755
|
+
var s = await _issued({ resolveKid: function () { return holder.publicKey; } }); // KeyObject
|
|
756
|
+
var proof = _makeProof({ keyPair: holder, aud: ISSUER_URL, nonce: s.cNonce,
|
|
757
|
+
headerOverride: { kid: "holder-kid-1" }, headerDelete: ["jwk"] });
|
|
758
|
+
var cred = await _issue(s, proof.jwt);
|
|
759
|
+
check("proof: kid → resolved KeyObject verifies and issues", cred.format === "vc+sd-jwt");
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
async function testKidResolverJwk() {
|
|
763
|
+
var holder = _newEcKey("P-256");
|
|
764
|
+
var s = await _issued({ resolveKid: function () { return _jwk(holder.publicKey); } }); // JWK
|
|
765
|
+
var proof = _makeProof({ keyPair: holder, aud: ISSUER_URL, nonce: s.cNonce,
|
|
766
|
+
headerOverride: { kid: "holder-kid-1" }, headerDelete: ["jwk"] });
|
|
767
|
+
var cred = await _issue(s, proof.jwt);
|
|
768
|
+
check("proof: kid → resolved JWK verifies and issues", cred.format === "vc+sd-jwt");
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
async function testKidResolverAlgKtyMismatch() {
|
|
772
|
+
// resolveKid hands back an EC key but the proof header declares EdDSA
|
|
773
|
+
// (requires kty=OKP) → CVE-2026-22817 alg-confusion cross-check refuses.
|
|
774
|
+
var ecHolder = _newEcKey("P-256");
|
|
775
|
+
var s = await _issued({ resolveKid: function () { return _jwk(ecHolder.publicKey); } });
|
|
776
|
+
var proof = _craftProof({ alg: "EdDSA", kid: "holder-kid-1" }, { nonce: s.cNonce });
|
|
777
|
+
await _expectThrow("proof: resolved-key alg/kty mismatch refused",
|
|
778
|
+
"auth-jwt-external/alg-kty-mismatch",
|
|
779
|
+
function () { return _issue(s, proof); });
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// ---- _verifyProofJwt: x5c parse guards ----
|
|
783
|
+
|
|
784
|
+
async function testX5cNotArrayOrEmpty() {
|
|
785
|
+
var s = await _issued();
|
|
786
|
+
await _expectThrow("proof: empty x5c array rejected", "auth-oid4vci/bad-x5c",
|
|
787
|
+
function () { return _issue(s, _craftProof({ x5c: [] }, { nonce: s.cNonce })); });
|
|
788
|
+
var s2 = await _issued();
|
|
789
|
+
await _expectThrow("proof: non-array x5c rejected", "auth-oid4vci/bad-x5c",
|
|
790
|
+
function () { return _issue(s2, _craftProof({ x5c: "not-an-array" }, { nonce: s2.cNonce })); });
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
async function testX5cBadEntry() {
|
|
794
|
+
var s = await _issued();
|
|
795
|
+
await _expectThrow("proof: non-string x5c entry rejected", "auth-oid4vci/bad-x5c",
|
|
796
|
+
function () { return _issue(s, _craftProof({ x5c: [123] }, { nonce: s.cNonce })); });
|
|
797
|
+
var s2 = await _issued();
|
|
798
|
+
await _expectThrow("proof: empty x5c entry rejected", "auth-oid4vci/bad-x5c",
|
|
799
|
+
function () { return _issue(s2, _craftProof({ x5c: [""] }, { nonce: s2.cNonce })); });
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
async function testX5cNonBase64() {
|
|
803
|
+
var s = await _issued();
|
|
804
|
+
// base64url-only characters ('-', '_') are invalid standard base64 (RFC 7515 §4.1.6)
|
|
805
|
+
await _expectThrow("proof: base64url chars in x5c rejected", "auth-oid4vci/bad-x5c",
|
|
806
|
+
function () { return _issue(s, _craftProof({ x5c: ["abcd-efgh_"] }, { nonce: s.cNonce })); });
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
async function testX5cBadBase64RoundTrip() {
|
|
810
|
+
var s = await _issued();
|
|
811
|
+
// 'AB' is in the standard alphabet but does not round-trip through base64.
|
|
812
|
+
await _expectThrow("proof: non-round-tripping x5c base64 rejected", "auth-oid4vci/bad-x5c",
|
|
813
|
+
function () { return _issue(s, _craftProof({ x5c: ["AB"] }, { nonce: s.cNonce })); });
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
async function testX5cNotDerCert() {
|
|
817
|
+
var s = await _issued();
|
|
818
|
+
var notACert = Buffer.from("this is valid base64 but not a DER certificate", "utf8").toString("base64");
|
|
819
|
+
await _expectThrow("proof: unparseable DER cert in x5c rejected", "auth-oid4vci/bad-x5c",
|
|
820
|
+
function () { return _issue(s, _craftProof({ x5c: [notACert] }, { nonce: s.cNonce })); });
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// ---- _verifyProofJwt: alg / signature ----
|
|
824
|
+
|
|
825
|
+
async function testProofAlgKtyMismatchInline() {
|
|
826
|
+
var s = await _issued();
|
|
827
|
+
// Inline EC jwk with an EdDSA-declaring header → alg-confusion cross-check.
|
|
828
|
+
var proof = _craftProof({ alg: "EdDSA", jwk: _ecJwk }, { nonce: s.cNonce });
|
|
829
|
+
await _expectThrow("proof: inline jwk alg/kty mismatch refused",
|
|
830
|
+
"auth-jwt-external/alg-kty-mismatch",
|
|
831
|
+
function () { return _issue(s, proof); });
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
async function testProofAlgAllowedButUnhashable() {
|
|
835
|
+
// ML-DSA-65 passes the issuer allowlist and the alg/kty cross-check (AKP
|
|
836
|
+
// key) and imports, but has no entry in the JWS hash map → refused.
|
|
837
|
+
var mldsa = nodeCrypto.generateKeyPairSync("ml-dsa-65");
|
|
838
|
+
var s = await _issued({ proofAlgorithms: ["ML-DSA-65"] });
|
|
839
|
+
var proof = _craftProof({ alg: "ML-DSA-65", jwk: _jwk(mldsa.publicKey) }, { nonce: s.cNonce });
|
|
840
|
+
await _expectThrow("proof: allowed-but-unhashable alg refused", "auth-oid4vci/unsupported-proof-alg",
|
|
841
|
+
function () { return _issue(s, proof); });
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
async function testProofBadSignature() {
|
|
845
|
+
var s = await _issued();
|
|
846
|
+
var keyA = _newEcKey("P-256");
|
|
847
|
+
var keyB = _newEcKey("P-256");
|
|
848
|
+
// Header advertises keyA, but the JWS is signed by keyB → signature fails.
|
|
849
|
+
var proof = _makeProof({ keyPair: keyA, signWith: keyB.privateKey, aud: ISSUER_URL, nonce: s.cNonce });
|
|
850
|
+
await _expectThrow("proof: bad signature rejected (holder does not hold the bound key)",
|
|
851
|
+
"auth-oid4vci/proof-bad-signature",
|
|
852
|
+
function () { return _issue(s, proof.jwt); });
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
async function testProofEdDSAHappy() {
|
|
856
|
+
var holder = nodeCrypto.generateKeyPairSync("ed25519");
|
|
857
|
+
var s = await _issued(); // EdDSA is in the default proofAlgorithms
|
|
858
|
+
var proof = _makeProof({ keyPair: holder, alg: "EdDSA", aud: ISSUER_URL, nonce: s.cNonce });
|
|
859
|
+
var cred = await _issue(s, proof.jwt);
|
|
860
|
+
check("proof: EdDSA holder proof verifies and issues", cred.format === "vc+sd-jwt");
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
async function testProofES384Happy() {
|
|
864
|
+
var holder = _newEcKey("P-384");
|
|
865
|
+
var s = await _issued({ proofAlgorithms: ["ES384"] });
|
|
866
|
+
var proof = _makeProof({ keyPair: holder, alg: "ES384", aud: ISSUER_URL, nonce: s.cNonce });
|
|
867
|
+
var cred = await _issue(s, proof.jwt);
|
|
868
|
+
check("proof: ES384 holder proof verifies and issues", cred.format === "vc+sd-jwt");
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
async function testProofPS256Happy() {
|
|
872
|
+
var holder = nodeCrypto.generateKeyPairSync("rsa", { modulusLength: 2048 });
|
|
873
|
+
var s = await _issued({ proofAlgorithms: ["PS256"] });
|
|
874
|
+
var proof = _makeProof({ keyPair: holder, alg: "PS256", aud: ISSUER_URL, nonce: s.cNonce });
|
|
875
|
+
var cred = await _issue(s, proof.jwt);
|
|
876
|
+
check("proof: PS256 (RSA-PSS) holder proof verifies and issues", cred.format === "vc+sd-jwt");
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// ---- _verifyProofJwt: x5c (RFC 7515 §4.1.6) cert-bound holder key ----
|
|
880
|
+
|
|
881
|
+
async function testX5cCertBoundPaths() {
|
|
882
|
+
// A real leaf certificate whose SPKI is the holder key, minted by the
|
|
883
|
+
// framework's own CA (b.mtlsCa) so the proof can be signed by the matching
|
|
884
|
+
// private key. No network — the CA is local, keyed off a temp data dir.
|
|
885
|
+
var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-oid4vci-x5c-"));
|
|
886
|
+
try {
|
|
887
|
+
var ca = b.mtlsCa.create({ dataDir: dir, caKeySealedMode: "disabled" });
|
|
888
|
+
var leaf = await ca.generateClientCert({ cn: "holder.wallet.local", validityDays: 7 });
|
|
889
|
+
var leafPriv = nodeCrypto.createPrivateKey(leaf.key); // EC P-384
|
|
890
|
+
var x5cEntry = new nodeCrypto.X509Certificate(leaf.cert).raw.toString("base64");
|
|
891
|
+
|
|
892
|
+
function _x5cProof(nonce) {
|
|
893
|
+
var nowSec = Math.floor(Date.now() / 1000);
|
|
894
|
+
var header = { typ: "openid4vci-proof+jwt", alg: "ES384", x5c: [x5cEntry] };
|
|
895
|
+
var payload = { aud: ISSUER_URL, nonce: nonce, iat: nowSec };
|
|
896
|
+
return _signProof(header, payload, leafPriv, "ES384"); // P-384 → ES384
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// (1) Happy: leaf-cert SPKI binds the holder key; validateX5c runs; verifies.
|
|
900
|
+
var seen = null;
|
|
901
|
+
var s1 = await _issued({
|
|
902
|
+
proofAlgorithms: ["ES384"],
|
|
903
|
+
validateX5c: function (chainDers, header) {
|
|
904
|
+
seen = { n: chainDers.length, isBuf: Buffer.isBuffer(chainDers[0]), typ: header.typ };
|
|
905
|
+
},
|
|
906
|
+
});
|
|
907
|
+
var cred = await _issue(s1, _x5cProof(s1.cNonce));
|
|
908
|
+
check("proof: x5c leaf-cert-bound proof verifies and issues", cred.format === "vc+sd-jwt");
|
|
909
|
+
check("proof: validateX5c received the parsed DER chain buffers",
|
|
910
|
+
seen && seen.n === 1 && seen.isBuf === true && seen.typ === "openid4vci-proof+jwt");
|
|
911
|
+
|
|
912
|
+
// (2) validateX5c throws a plain Error → wrapped as a typed x5c-rejected.
|
|
913
|
+
var s2 = await _issued({ proofAlgorithms: ["ES384"],
|
|
914
|
+
validateX5c: function () { throw new Error("untrusted attestation CA"); } });
|
|
915
|
+
await _expectThrow("proof: validateX5c plain-throw wrapped as x5c-rejected",
|
|
916
|
+
"auth-oid4vci/x5c-rejected",
|
|
917
|
+
function () { return _issue(s2, _x5cProof(s2.cNonce)); });
|
|
918
|
+
|
|
919
|
+
// (3) validateX5c throws an AuthError → preserved verbatim, not re-wrapped.
|
|
920
|
+
var s3 = await _issued({ proofAlgorithms: ["ES384"],
|
|
921
|
+
validateX5c: function () { throw new AuthError("auth-oid4vci/custom-x5c-policy", "revoked leaf"); } });
|
|
922
|
+
var err3 = await _expectThrow("proof: validateX5c AuthError preserved verbatim",
|
|
923
|
+
"auth-oid4vci/custom-x5c-policy",
|
|
924
|
+
function () { return _issue(s3, _x5cProof(s3.cNonce)); });
|
|
925
|
+
check("proof: validateX5c AuthError not re-wrapped as x5c-rejected",
|
|
926
|
+
err3 && err3.code === "auth-oid4vci/custom-x5c-policy");
|
|
927
|
+
} finally {
|
|
928
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// ---- Run all ----
|
|
933
|
+
|
|
934
|
+
async function run() {
|
|
935
|
+
await testFullFlowHappyPath();
|
|
936
|
+
await testMetadataDocument();
|
|
937
|
+
await testMetadataCustomFields();
|
|
938
|
+
|
|
939
|
+
testCreateRequireObject();
|
|
940
|
+
testCreateMissingRequiredStrings();
|
|
941
|
+
testCreateNoSdJwtIssuer();
|
|
942
|
+
testCreateNoSupportedCredentials();
|
|
943
|
+
testCreateBadCredentialSpec();
|
|
944
|
+
testCreateUnsupportedFormat();
|
|
945
|
+
testCreateNoVct();
|
|
946
|
+
testCreateBadResolveKidType();
|
|
947
|
+
|
|
948
|
+
await testOfferBadSubject();
|
|
949
|
+
await testOfferBadCredentialIds();
|
|
950
|
+
await testOfferUnknownCredentialId();
|
|
951
|
+
await testOfferBadTxCode();
|
|
952
|
+
await testOfferTxCodeDefaults();
|
|
953
|
+
await testOfferTxCodeExplicit();
|
|
954
|
+
|
|
955
|
+
await testExchangeMissingCode();
|
|
956
|
+
await testExchangeUnknownCode();
|
|
957
|
+
await testExchangeTxCodeRequired();
|
|
958
|
+
await testExchangeTxCodeMismatch();
|
|
959
|
+
await testExchangeAlreadyRedeemedRace();
|
|
960
|
+
|
|
961
|
+
await testIssueMissingAccessToken();
|
|
962
|
+
await testIssueInvalidAccessToken();
|
|
963
|
+
await testIssueWrongCredentialIdentifier();
|
|
964
|
+
await testIssueUnknownCredentialId();
|
|
965
|
+
await testIssueNoClaims();
|
|
966
|
+
await testIssueAccessTokenConsumedRace();
|
|
967
|
+
await testIssueMintThrowsRestoresToken();
|
|
968
|
+
await testIssueSingleUseOptOut();
|
|
969
|
+
await testIssueCustomDisclosureAndTtl();
|
|
970
|
+
await testIssueProofMaxAgeCustom();
|
|
971
|
+
|
|
972
|
+
await testProofEmptyOrTooLong();
|
|
973
|
+
await testProofNotThreeParts();
|
|
974
|
+
await testProofBadDecode();
|
|
975
|
+
await testProofWrongTyp();
|
|
976
|
+
await testProofUnsupportedAlgAllowlist();
|
|
977
|
+
await testProofCritRefused();
|
|
978
|
+
await testProofNoKey();
|
|
979
|
+
await testProofWrongAud();
|
|
980
|
+
await testProofCNonceExpired();
|
|
981
|
+
await testProofWrongNonce();
|
|
982
|
+
await testProofCNonceNullStoreMissFailsClosed();
|
|
983
|
+
await testProofNoIat();
|
|
984
|
+
await testProofIatFuture();
|
|
985
|
+
await testProofIatTooOld();
|
|
986
|
+
|
|
987
|
+
await testKidNoResolver();
|
|
988
|
+
await testKidResolverThrows();
|
|
989
|
+
await testKidResolverUnresolved();
|
|
990
|
+
await testKidResolverBadShape();
|
|
991
|
+
await testKidResolverKeyObject();
|
|
992
|
+
await testKidResolverJwk();
|
|
993
|
+
await testKidResolverAlgKtyMismatch();
|
|
994
|
+
|
|
995
|
+
await testX5cNotArrayOrEmpty();
|
|
996
|
+
await testX5cBadEntry();
|
|
997
|
+
await testX5cNonBase64();
|
|
998
|
+
await testX5cBadBase64RoundTrip();
|
|
999
|
+
await testX5cNotDerCert();
|
|
1000
|
+
|
|
1001
|
+
await testProofAlgKtyMismatchInline();
|
|
1002
|
+
await testProofAlgAllowedButUnhashable();
|
|
1003
|
+
await testProofBadSignature();
|
|
1004
|
+
await testProofEdDSAHappy();
|
|
1005
|
+
await testProofES384Happy();
|
|
1006
|
+
await testProofPS256Happy();
|
|
1007
|
+
|
|
1008
|
+
await testX5cCertBoundPaths();
|
|
1009
|
+
|
|
1010
|
+
if (require.main === module) {
|
|
1011
|
+
console.log("oid4vci.test.js: " + helpers.getChecks() + " checks passed");
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
module.exports = { run: run };
|
|
1016
|
+
if (require.main === module) run().catch(function (e) { console.error(e); process.exit(1); });
|