@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
|
@@ -26,6 +26,16 @@ var nodeCrypto = require("crypto");
|
|
|
26
26
|
|
|
27
27
|
var mailCrypto = require("../../lib/mail-crypto");
|
|
28
28
|
var smime = mailCrypto.smime;
|
|
29
|
+
var b = helpers.b;
|
|
30
|
+
|
|
31
|
+
var asn1 = require("../../lib/asn1-der");
|
|
32
|
+
var cms = require("../../lib/cms-codec");
|
|
33
|
+
var pqc = require("../../lib/pqc-software");
|
|
34
|
+
var spawnSync = require("child_process").spawnSync;
|
|
35
|
+
|
|
36
|
+
var OID = cms.OID;
|
|
37
|
+
var OID_MD_ATTR = "1.2.840.113549.1.9.4"; // RFC 5652 §11.2 messageDigest
|
|
38
|
+
var OID_CT_ATTR = "1.2.840.113549.1.9.3"; // RFC 5652 §11.1 contentType
|
|
29
39
|
|
|
30
40
|
// ---- Helper: minimal self-signed certificate (node:crypto only) ----
|
|
31
41
|
//
|
|
@@ -320,6 +330,834 @@ function testSmimeContentTypeAttrExtraction() {
|
|
|
320
330
|
smime._extractContentTypeOid(withoutCt) === null);
|
|
321
331
|
}
|
|
322
332
|
|
|
333
|
+
// ---- b.mail.crypto.smime.verifyAll — multi-signer envelopes ----
|
|
334
|
+
//
|
|
335
|
+
// verifyAll walks EVERY SignerInfo in a CMS SignedData and verifies each
|
|
336
|
+
// against the matching key in signerPublicKeys, a map keyed by the
|
|
337
|
+
// SignerInfo's issuerAndSerialNumber serial-hex. smime.sign() only mints
|
|
338
|
+
// single-signer envelopes, so a genuine two-signer envelope is built
|
|
339
|
+
// directly via b.cms.encodeSignedData to exercise the multi-signer path.
|
|
340
|
+
|
|
341
|
+
function _certDerWithSerial(serialByte) {
|
|
342
|
+
var asn1 = require("../../lib/asn1-der");
|
|
343
|
+
var serial = asn1.writeInteger(Buffer.from([serialByte]));
|
|
344
|
+
var sigAlg = asn1.writeNode(0x30, asn1.writeOid("2.16.840.1.101.3.4.3.18"));
|
|
345
|
+
var issuer = asn1.writeNode(0x30, asn1.writeOid("2.5.4.3"));
|
|
346
|
+
var tbs = asn1.writeNode(0x30, Buffer.concat([serial, sigAlg, issuer]));
|
|
347
|
+
var fakeSigAlg = asn1.writeNode(0x30, asn1.writeOid("2.16.840.1.101.3.4.3.18"));
|
|
348
|
+
var fakeSig = asn1.writeNode(0x03, Buffer.from([0x00, 0x00, 0x01]));
|
|
349
|
+
return asn1.writeNode(0x30, Buffer.concat([tbs, fakeSigAlg, fakeSig]));
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Two-signer detached CMS SignedData over `msg`; certs carry serials
|
|
353
|
+
// 0x01 / 0x02 so verifyAll extracts serial-hex "01" / "02".
|
|
354
|
+
function _twoSignerEnvelope(msg) {
|
|
355
|
+
var pq = require("../../lib/pqc-software");
|
|
356
|
+
var kp1 = pq.ml_dsa_65.keygen();
|
|
357
|
+
var kp2 = pq.ml_dsa_65.keygen();
|
|
358
|
+
var sd = b.cms.encodeSignedData({
|
|
359
|
+
encapContent: msg, digestAlg: "sha3-512", detached: true,
|
|
360
|
+
signers: [
|
|
361
|
+
{ certificate: _certDerWithSerial(0x01), secretKey: kp1.secretKey, sigAlg: "ML-DSA-65" },
|
|
362
|
+
{ certificate: _certDerWithSerial(0x02), secretKey: kp2.secretKey, sigAlg: "ML-DSA-65" },
|
|
363
|
+
],
|
|
364
|
+
});
|
|
365
|
+
return { signature: sd, pub1: kp1.publicKey, pub2: kp2.publicKey };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function testVerifyAllMultiSigner() {
|
|
369
|
+
var msg = Buffer.from("multi-signer body");
|
|
370
|
+
var env = _twoSignerEnvelope(msg);
|
|
371
|
+
var v = b.mail.crypto.smime.verifyAll({
|
|
372
|
+
message: msg, signature: env.signature,
|
|
373
|
+
signerPublicKeys: { "01": env.pub1, "02": env.pub2 },
|
|
374
|
+
});
|
|
375
|
+
check("verifyAll: valid true when every signer verifies", v.valid === true);
|
|
376
|
+
check("verifyAll: reports both signers", v.signers.length === 2);
|
|
377
|
+
var serials = v.signers.map(function (s) { return s.serialHex; }).sort();
|
|
378
|
+
check("verifyAll: surfaces both signer serials ('01','02')",
|
|
379
|
+
serials[0] === "01" && serials[1] === "02");
|
|
380
|
+
check("verifyAll: reports the PQC sigAlg per signer",
|
|
381
|
+
v.signers.every(function (s) { return s.sigAlg === "ML-DSA-65"; }));
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// The advertised guarantee: each SignerInfo is verified against its OWN
|
|
385
|
+
// key. Swapping the two keys must be refused (each signature checked
|
|
386
|
+
// against the wrong key), proving per-signer binding — not a reuse of
|
|
387
|
+
// signerInfos[0]'s key for every signer.
|
|
388
|
+
function testVerifyAllPerSignerKeyBinding() {
|
|
389
|
+
var msg = Buffer.from("bind-each-signer");
|
|
390
|
+
var env = _twoSignerEnvelope(msg);
|
|
391
|
+
var threw = null;
|
|
392
|
+
try {
|
|
393
|
+
b.mail.crypto.smime.verifyAll({
|
|
394
|
+
message: msg, signature: env.signature,
|
|
395
|
+
signerPublicKeys: { "01": env.pub2, "02": env.pub1 }, // swapped
|
|
396
|
+
});
|
|
397
|
+
} catch (e) { threw = e; }
|
|
398
|
+
check("verifyAll: swapped per-signer keys refused (signature-mismatch)",
|
|
399
|
+
threw && threw.code === "mail-crypto/smime/signature-mismatch");
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function testVerifyAllMissingKey() {
|
|
403
|
+
var msg = Buffer.from("missing-key-case");
|
|
404
|
+
var env = _twoSignerEnvelope(msg);
|
|
405
|
+
var threw = null;
|
|
406
|
+
try {
|
|
407
|
+
b.mail.crypto.smime.verifyAll({
|
|
408
|
+
message: msg, signature: env.signature,
|
|
409
|
+
signerPublicKeys: { "01": env.pub1 }, // no key for serial "02"
|
|
410
|
+
});
|
|
411
|
+
} catch (e) { threw = e; }
|
|
412
|
+
check("verifyAll: a SignerInfo with no supplied key throws missing-key",
|
|
413
|
+
threw && threw.code === "mail-crypto/smime/missing-key");
|
|
414
|
+
check("verifyAll: missing-key names the unresolved serial",
|
|
415
|
+
threw && /02/.test(threw.message || ""));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function testVerifyAllTamperRefused() {
|
|
419
|
+
var msg = Buffer.from("original signed bytes");
|
|
420
|
+
var env = _twoSignerEnvelope(msg);
|
|
421
|
+
var threw = null;
|
|
422
|
+
try {
|
|
423
|
+
b.mail.crypto.smime.verifyAll({
|
|
424
|
+
message: Buffer.from("TAMPERED bytes"), signature: env.signature,
|
|
425
|
+
signerPublicKeys: { "01": env.pub1, "02": env.pub2 },
|
|
426
|
+
});
|
|
427
|
+
} catch (e) { threw = e; }
|
|
428
|
+
check("verifyAll: tampered message refused with message-digest-mismatch",
|
|
429
|
+
threw && threw.code === "mail-crypto/smime/message-digest-mismatch");
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// A single-signer envelope minted by smime.sign() must also verify
|
|
433
|
+
// through verifyAll — the consumer path for operators who always route
|
|
434
|
+
// through verifyAll regardless of signer count. The signer cert (serial
|
|
435
|
+
// 0x01) keys the map.
|
|
436
|
+
function testVerifyAllSingleSigner() {
|
|
437
|
+
var pq = require("../../lib/pqc-software");
|
|
438
|
+
var kp = pq.ml_dsa_65.keygen();
|
|
439
|
+
var msg = "From: x@y\r\nSubject: one\r\n\r\nbody";
|
|
440
|
+
var out = smime.sign({
|
|
441
|
+
message: msg, certificate: _testCertDer(),
|
|
442
|
+
secretKey: kp.secretKey, sigAlg: "ML-DSA-65",
|
|
443
|
+
});
|
|
444
|
+
var v = b.mail.crypto.smime.verifyAll({
|
|
445
|
+
message: msg, signature: out.signature,
|
|
446
|
+
signerPublicKeys: { "01": kp.publicKey },
|
|
447
|
+
});
|
|
448
|
+
check("verifyAll: single-signer sign()-produced envelope verifies",
|
|
449
|
+
v.valid === true && v.signers.length === 1);
|
|
450
|
+
check("verifyAll: single-signer serial is '01'", v.signers[0].serialHex === "01");
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function testVerifyAllInputValidation() {
|
|
454
|
+
function shouldThrow(label, opts) {
|
|
455
|
+
var threw = null;
|
|
456
|
+
try { b.mail.crypto.smime.verifyAll(opts); } catch (e) { threw = e; }
|
|
457
|
+
check("verifyAll validate: " + label,
|
|
458
|
+
threw && threw.code === "mail-crypto/smime/bad-opts");
|
|
459
|
+
}
|
|
460
|
+
shouldThrow("null opts", null);
|
|
461
|
+
shouldThrow("signerPublicKeys not a map",
|
|
462
|
+
{ message: Buffer.from("x"), signature: Buffer.from("x"), signerPublicKeys: 5 });
|
|
463
|
+
shouldThrow("message missing",
|
|
464
|
+
{ signature: Buffer.from("x"), signerPublicKeys: {} });
|
|
465
|
+
shouldThrow("signature not a Buffer",
|
|
466
|
+
{ message: Buffer.from("x"), signature: "notbuf", signerPublicKeys: {} });
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ============================================================================
|
|
470
|
+
// Uncovered error / adversarial / defensive branch coverage.
|
|
471
|
+
//
|
|
472
|
+
// These exercise the fail-closed guards in sign() / verify() /
|
|
473
|
+
// _verifySignerInfo / _verifyTrustChain / checkCert that the happy-path
|
|
474
|
+
// roundtrip tests above never reach. Two fixture machineries drive them:
|
|
475
|
+
//
|
|
476
|
+
// 1. asn1-level CMS SignedData crafting (`_craftSignedData`) — builds a
|
|
477
|
+
// malformed SignedData that b.cms.parseSignedData accepts but whose
|
|
478
|
+
// SignerInfo carries an unknown sigAlg / digest OID, absent signed-
|
|
479
|
+
// attrs, a missing messageDigest / contentType attribute, etc. These
|
|
480
|
+
// reach _verifySignerInfo's typed refusals with no key material.
|
|
481
|
+
// 2. openssl-minted certificates (`_mintCertMinimal` / `_withMlDsaCa`) —
|
|
482
|
+
// real X.509 certs (RSA / EC for checkCert; ML-DSA-65 for the trust-
|
|
483
|
+
// anchor chain walk). ML-DSA CMS signatures are produced by openssl and
|
|
484
|
+
// verified by the vendored pqc-software (FIPS 204 interop), which is the
|
|
485
|
+
// only way to feed _verifyTrustChain a node-parseable cert whose SPKI
|
|
486
|
+
// equals the PQC signer key. Every openssl path degrades to a recorded
|
|
487
|
+
// skip when the toolchain lacks the algorithm, so the suite still
|
|
488
|
+
// exits 0 on a host without ML-DSA support.
|
|
489
|
+
// ============================================================================
|
|
490
|
+
|
|
491
|
+
function _algId(oid) { return asn1.writeSequence([asn1.writeOid(oid)]); }
|
|
492
|
+
function _attr(oid, val) { return asn1.writeSequence([asn1.writeOid(oid), asn1.writeSet([val])]); }
|
|
493
|
+
function _issuerSerialSid(serialByte) {
|
|
494
|
+
return asn1.writeSequence([
|
|
495
|
+
asn1.writeSequence([asn1.writeOid("2.5.4.3")]), // issuer Name
|
|
496
|
+
asn1.writeInteger(Buffer.from([serialByte])), // serialNumber
|
|
497
|
+
]);
|
|
498
|
+
}
|
|
499
|
+
function _sha3_512(bytes) { return nodeCrypto.createHash("sha3-512").update(bytes).digest(); }
|
|
500
|
+
|
|
501
|
+
// Build a ContentInfo → SignedData DER with a single controllable SignerInfo.
|
|
502
|
+
// opts: { digestOid, sigOid, eContentType, sid, signedAttrsImplicit (or null
|
|
503
|
+
// to omit), signature, certsDer, signerInfos (raw SET member list — overrides
|
|
504
|
+
// the single-signer build) }.
|
|
505
|
+
function _craftSignedData(o) {
|
|
506
|
+
o = o || {};
|
|
507
|
+
var digestOid = o.digestOid || OID.sha3_512;
|
|
508
|
+
var sigOid = o.sigOid || OID.mldsa65;
|
|
509
|
+
var eContentType = o.eContentType || OID.data;
|
|
510
|
+
var encap = asn1.writeSequence([asn1.writeOid(eContentType)]); // detached — eContentType only
|
|
511
|
+
var siList;
|
|
512
|
+
if (o.signerInfos) {
|
|
513
|
+
siList = o.signerInfos;
|
|
514
|
+
} else {
|
|
515
|
+
var siChildren = [asn1.writeInteger(Buffer.from([1])), o.sid || _issuerSerialSid(0x01), _algId(digestOid)];
|
|
516
|
+
if (o.signedAttrsImplicit !== null && o.signedAttrsImplicit !== undefined) {
|
|
517
|
+
siChildren.push(o.signedAttrsImplicit);
|
|
518
|
+
}
|
|
519
|
+
siChildren.push(_algId(sigOid));
|
|
520
|
+
siChildren.push(asn1.writeOctetString(o.signature || Buffer.from([0, 0, 1])));
|
|
521
|
+
siList = [asn1.writeSequence(siChildren)];
|
|
522
|
+
}
|
|
523
|
+
var sdChildren = [asn1.writeInteger(Buffer.from([1])), asn1.writeSet([_algId(digestOid)]), encap];
|
|
524
|
+
if (o.certsDer && o.certsDer.length) {
|
|
525
|
+
sdChildren.push(asn1.writeContextExplicit(0, Buffer.concat(o.certsDer)));
|
|
526
|
+
}
|
|
527
|
+
sdChildren.push(asn1.writeSet(siList));
|
|
528
|
+
var sd = asn1.writeSequence(sdChildren);
|
|
529
|
+
return asn1.writeSequence([asn1.writeOid(OID.signedData), asn1.writeContextExplicit(0, sd)]);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// A signedAttrs SET re-tagged [0] IMPLICIT for the SignerInfo. Returns both the
|
|
533
|
+
// universal-SET form (what the signature covers) and the [0] IMPLICIT form (what
|
|
534
|
+
// the SignerInfo embeds). Byte-identical apart from the leading tag, so a
|
|
535
|
+
// signature over `.set` is what verify() reconstructs.
|
|
536
|
+
function _signedAttrs(attrBufs) {
|
|
537
|
+
var set = asn1.writeSet(attrBufs);
|
|
538
|
+
return { set: set, implicit: Buffer.concat([Buffer.from([0xa0]), set.slice(1)]) };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// ---- openssl cert minting (RSA / EC, minimal config) ----
|
|
542
|
+
//
|
|
543
|
+
// OpenSSL 3.5's default req config trips on its v3_ca extension section; a
|
|
544
|
+
// minimal inline config avoids it. Returns { pem, der } or null when openssl
|
|
545
|
+
// is unavailable / the mint fails, so callers can record a graceful skip.
|
|
546
|
+
function _mintCertMinimal(opts) {
|
|
547
|
+
var fs = require("fs"), os = require("os"), path = require("path");
|
|
548
|
+
var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-smime-mint-"));
|
|
549
|
+
try {
|
|
550
|
+
var keyShape = opts.alg === "ec"
|
|
551
|
+
? { namedCurve: opts.curve || "P-256" }
|
|
552
|
+
: { modulusLength: opts.bits || 2048 };
|
|
553
|
+
var kp = nodeCrypto.generateKeyPairSync(opts.alg, Object.assign({
|
|
554
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
555
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
556
|
+
}, keyShape));
|
|
557
|
+
var keyPath = path.join(dir, "k.pem"), certPath = path.join(dir, "c.pem"), cfgPath = path.join(dir, "m.cnf");
|
|
558
|
+
fs.writeFileSync(keyPath, kp.privateKey);
|
|
559
|
+
fs.writeFileSync(cfgPath,
|
|
560
|
+
"[req]\ndistinguished_name=dn\nx509_extensions=v3\nprompt=no\n" +
|
|
561
|
+
"[dn]\nCN=" + (opts.cn || "smime-test.example") + "\n" +
|
|
562
|
+
"[v3]\nbasicConstraints=critical,CA:TRUE\n");
|
|
563
|
+
var args = ["req", "-x509", "-config", cfgPath, "-key", keyPath, "-out", certPath, "-" + (opts.hash || "sha256")];
|
|
564
|
+
if (opts.notBefore) { args.push("-not_before", opts.notBefore, "-not_after", opts.notAfter); }
|
|
565
|
+
else { args.push("-days", String(opts.days || 30)); }
|
|
566
|
+
var rv = spawnSync("openssl", args, { stdio: "pipe" });
|
|
567
|
+
if (rv.status !== 0) return null;
|
|
568
|
+
var pem = fs.readFileSync(certPath, "utf8");
|
|
569
|
+
var der = Buffer.from(pem.replace(/-----[^-]+-----/g, "").replace(/\s+/g, ""), "base64");
|
|
570
|
+
return { pem: pem, der: der };
|
|
571
|
+
} catch (_e) {
|
|
572
|
+
return null;
|
|
573
|
+
} finally {
|
|
574
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_e2) { /* ignore */ }
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
var _opensslCap = null;
|
|
579
|
+
function _opensslAvailable() {
|
|
580
|
+
if (_opensslCap === null) {
|
|
581
|
+
_opensslCap = _mintCertMinimal({ alg: "rsa", bits: 2048, hash: "sha256", days: 1 }) !== null;
|
|
582
|
+
}
|
|
583
|
+
return _opensslCap;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// ---- openssl ML-DSA certificate authority (self-signed + CA-issued) ----
|
|
587
|
+
//
|
|
588
|
+
// Runs `fn(toolkit)` inside a throwaway workspace directory, then removes it.
|
|
589
|
+
// The toolkit mints ML-DSA-65 self-signed and CA-issued certs and raw-signs
|
|
590
|
+
// bytes with an ML-DSA key. Returns null from a mint when the openssl build
|
|
591
|
+
// lacks ML-DSA.
|
|
592
|
+
function _withMlDsaCa(fn) {
|
|
593
|
+
var fs = require("fs"), os = require("os"), path = require("path");
|
|
594
|
+
var dir = fs.mkdtempSync(path.join(os.tmpdir(), "blamejs-smime-mldsa-"));
|
|
595
|
+
var seq = 0;
|
|
596
|
+
function name(tag) { seq += 1; return path.join(dir, tag + seq); }
|
|
597
|
+
function genKey() {
|
|
598
|
+
var k = name("key") + ".pem";
|
|
599
|
+
return spawnSync("openssl", ["genpkey", "-algorithm", "ML-DSA-65", "-out", k], { stdio: "pipe" }).status === 0 ? k : null;
|
|
600
|
+
}
|
|
601
|
+
function load(certPath, keyPath) {
|
|
602
|
+
var pem = fs.readFileSync(certPath, "utf8");
|
|
603
|
+
var der = Buffer.from(pem.replace(/-----[^-]+-----/g, "").replace(/\s+/g, ""), "base64");
|
|
604
|
+
var cert = new nodeCrypto.X509Certificate(der);
|
|
605
|
+
var spki = Buffer.from(cert.publicKey.export({ format: "der", type: "spki" }));
|
|
606
|
+
return { pem: pem, der: der, cert: cert, certPath: certPath, keyPath: keyPath, rawPub: spki.subarray(spki.length - 1952) };
|
|
607
|
+
}
|
|
608
|
+
function selfSigned(cn, opts) {
|
|
609
|
+
opts = opts || {};
|
|
610
|
+
var key = genKey(); if (!key) return null;
|
|
611
|
+
var cfg = name("cfg") + ".cnf";
|
|
612
|
+
fs.writeFileSync(cfg, "[req]\ndistinguished_name=dn\nx509_extensions=v3\nprompt=no\n[dn]\nCN=" + cn + "\n[v3]\nbasicConstraints=critical,CA:TRUE\n");
|
|
613
|
+
var cert = name("cert") + ".pem";
|
|
614
|
+
var args = ["req", "-x509", "-config", cfg, "-key", key, "-out", cert];
|
|
615
|
+
if (opts.notBefore) { args.push("-not_before", opts.notBefore, "-not_after", opts.notAfter); }
|
|
616
|
+
else { args.push("-days", "3650"); }
|
|
617
|
+
if (spawnSync("openssl", args, { stdio: "pipe" }).status !== 0) return null;
|
|
618
|
+
return load(cert, key);
|
|
619
|
+
}
|
|
620
|
+
function caSign(cn, ca) {
|
|
621
|
+
var key = genKey(); if (!key) return null;
|
|
622
|
+
var rcfg = name("rcfg") + ".cnf";
|
|
623
|
+
fs.writeFileSync(rcfg, "[req]\ndistinguished_name=dn\nprompt=no\n[dn]\nCN=" + cn + "\n");
|
|
624
|
+
var csr = name("csr") + ".csr";
|
|
625
|
+
if (spawnSync("openssl", ["req", "-new", "-config", rcfg, "-key", key, "-out", csr], { stdio: "pipe" }).status !== 0) return null;
|
|
626
|
+
var ext = name("ext") + ".cnf";
|
|
627
|
+
fs.writeFileSync(ext, "basicConstraints=critical,CA:TRUE\n");
|
|
628
|
+
var cert = name("cert") + ".pem";
|
|
629
|
+
if (spawnSync("openssl", ["x509", "-req", "-in", csr, "-CA", ca.certPath, "-CAkey", ca.keyPath,
|
|
630
|
+
"-out", cert, "-days", "3650", "-extfile", ext, "-CAcreateserial"], { stdio: "pipe" }).status !== 0) return null;
|
|
631
|
+
return load(cert, key);
|
|
632
|
+
}
|
|
633
|
+
function rawSign(keyPath, bytes) {
|
|
634
|
+
var mp = name("msg") + ".bin"; fs.writeFileSync(mp, bytes);
|
|
635
|
+
var r = spawnSync("openssl", ["pkeyutl", "-sign", "-rawin", "-inkey", keyPath, "-in", mp], { stdio: ["pipe", "pipe", "pipe"] });
|
|
636
|
+
if (r.status !== 0) throw new Error("openssl ML-DSA sign failed");
|
|
637
|
+
return r.stdout;
|
|
638
|
+
}
|
|
639
|
+
try { return fn({ selfSigned: selfSigned, caSign: caSign, rawSign: rawSign }); }
|
|
640
|
+
finally { try { fs.rmSync(dir, { recursive: true, force: true }); } catch (_e) { /* ignore */ } }
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
var _mlDsaCap = null;
|
|
644
|
+
function _mlDsaAvailable() {
|
|
645
|
+
if (_mlDsaCap === null) {
|
|
646
|
+
_mlDsaCap = _withMlDsaCa(function (tk) { return tk.selfSigned("cap-probe") !== null; });
|
|
647
|
+
}
|
|
648
|
+
return _mlDsaCap;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// Detached ML-DSA CMS SignedData over `msg`, signed by openssl, embedding
|
|
652
|
+
// `certsDer` in SignedData.certificates. contentType + messageDigest signed-
|
|
653
|
+
// attrs are set so _verifySignerInfo's digest + content-type binding pass.
|
|
654
|
+
function _mlDsaEnvelope(tk, signerKeyPath, msg, certsDer) {
|
|
655
|
+
var sa = _signedAttrs([
|
|
656
|
+
_attr(OID_CT_ATTR, asn1.writeOid(OID.data)),
|
|
657
|
+
_attr(OID_MD_ATTR, asn1.writeOctetString(_sha3_512(msg))),
|
|
658
|
+
]);
|
|
659
|
+
var sig = tk.rawSign(signerKeyPath, sa.set);
|
|
660
|
+
var si = asn1.writeSequence([
|
|
661
|
+
asn1.writeInteger(Buffer.from([1])), _issuerSerialSid(0x01), _algId(OID.sha3_512),
|
|
662
|
+
sa.implicit, _algId(OID.mldsa65), asn1.writeOctetString(sig),
|
|
663
|
+
]);
|
|
664
|
+
return _craftSignedData({ signerInfos: [si], certsDer: certsDer });
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// ---- sign(): input validation + cms-encode failure ----
|
|
668
|
+
|
|
669
|
+
function testSmimeSignInputValidation() {
|
|
670
|
+
var kp = pqc.ml_dsa_65.keygen();
|
|
671
|
+
function refused(label, opts, codeRe) {
|
|
672
|
+
var threw = null;
|
|
673
|
+
try { smime.sign(opts); } catch (e) { threw = e; }
|
|
674
|
+
check("sign validate: " + label, threw && codeRe.test(String(threw.code || "")));
|
|
675
|
+
}
|
|
676
|
+
refused("certificate must be a Buffer",
|
|
677
|
+
{ message: "x", certificate: "not-a-buffer", secretKey: kp.secretKey, sigAlg: "ML-DSA-65" },
|
|
678
|
+
/smime\/bad-opts/);
|
|
679
|
+
refused("secretKey must be a Uint8Array",
|
|
680
|
+
{ message: "x", certificate: _testCertDer(), secretKey: "not-a-key", sigAlg: "ML-DSA-65" },
|
|
681
|
+
/smime\/bad-opts/);
|
|
682
|
+
refused("message must be Buffer or string",
|
|
683
|
+
{ message: 12345, certificate: _testCertDer(), secretKey: kp.secretKey, sigAlg: "ML-DSA-65" },
|
|
684
|
+
/smime\/bad-opts/);
|
|
685
|
+
// A well-formed opts bundle whose sigAlg the CMS substrate rejects surfaces
|
|
686
|
+
// as sign-failed (the encodeSignedData throw is caught + re-typed).
|
|
687
|
+
var threw = null;
|
|
688
|
+
try { smime.sign({ message: "x", certificate: _testCertDer(), secretKey: kp.secretKey, sigAlg: "RSA-2048" }); }
|
|
689
|
+
catch (e) { threw = e; }
|
|
690
|
+
check("sign: unsupported sigAlg surfaces as sign-failed",
|
|
691
|
+
threw && threw.code === "mail-crypto/smime/sign-failed");
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// ---- verify(): input validation + parse failure + zero signers ----
|
|
695
|
+
|
|
696
|
+
function testSmimeVerifyInputValidation() {
|
|
697
|
+
var kp = pqc.ml_dsa_65.keygen();
|
|
698
|
+
function refused(label, opts, code) {
|
|
699
|
+
var threw = null;
|
|
700
|
+
try { smime.verify(opts); } catch (e) { threw = e; }
|
|
701
|
+
check("verify validate: " + label, threw && threw.code === code);
|
|
702
|
+
}
|
|
703
|
+
refused("message missing",
|
|
704
|
+
{ signature: Buffer.from([1]), signerPublicKey: kp.publicKey }, "mail-crypto/smime/bad-opts");
|
|
705
|
+
refused("signature not a Buffer",
|
|
706
|
+
{ message: "x", signature: "nope", signerPublicKey: kp.publicKey }, "mail-crypto/smime/bad-opts");
|
|
707
|
+
refused("signerPublicKey not a Uint8Array",
|
|
708
|
+
{ message: "x", signature: Buffer.from([1]), signerPublicKey: "nope" }, "mail-crypto/smime/bad-opts");
|
|
709
|
+
refused("garbage CMS signature bytes rejected at parse",
|
|
710
|
+
{ message: "x", signature: Buffer.from("not valid der at all"), signerPublicKey: kp.publicKey },
|
|
711
|
+
"mail-crypto/smime/parse-failed");
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function testSmimeVerifyNoSigners() {
|
|
715
|
+
var kp = pqc.ml_dsa_65.keygen();
|
|
716
|
+
var env = _craftSignedData({ signerInfos: [] }); // SignerInfos SET is empty
|
|
717
|
+
var threw = null;
|
|
718
|
+
try { smime.verify({ message: "x", signature: env, signerPublicKey: kp.publicKey }); }
|
|
719
|
+
catch (e) { threw = e; }
|
|
720
|
+
check("verify: zero-SignerInfo envelope refused with no-signers",
|
|
721
|
+
threw && threw.code === "mail-crypto/smime/no-signers");
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// ---- _verifySignerInfo(): every typed per-signer refusal ----
|
|
725
|
+
|
|
726
|
+
function testSmimeVerifySignerInfoRefusals() {
|
|
727
|
+
var msg = Buffer.from("per-signer-refusal-body");
|
|
728
|
+
var goodMd = _attr(OID_MD_ATTR, asn1.writeOctetString(_sha3_512(msg)));
|
|
729
|
+
var goodCt = _attr(OID_CT_ATTR, asn1.writeOid(OID.data));
|
|
730
|
+
var dummyPub = new Uint8Array(1952);
|
|
731
|
+
function verifyCraft(label, craftOpts, code, pub) {
|
|
732
|
+
var threw = null;
|
|
733
|
+
try { smime.verify({ message: msg, signature: _craftSignedData(craftOpts), signerPublicKey: pub || dummyPub }); }
|
|
734
|
+
catch (e) { threw = e; }
|
|
735
|
+
check("verify signerInfo: " + label, threw && threw.code === code);
|
|
736
|
+
}
|
|
737
|
+
// sigAlg OID outside the PQC allowlist.
|
|
738
|
+
verifyCraft("unknown sigAlg OID refused",
|
|
739
|
+
{ sigOid: "1.2.3.4.5", signedAttrsImplicit: _signedAttrs([goodMd]).implicit },
|
|
740
|
+
"mail-crypto/smime/bad-sig-alg");
|
|
741
|
+
// digestAlg OID outside {sha3-256, sha3-512}.
|
|
742
|
+
verifyCraft("unknown digestAlg OID refused",
|
|
743
|
+
{ digestOid: "1.2.3.4.6", signedAttrsImplicit: _signedAttrs([goodMd]).implicit },
|
|
744
|
+
"mail-crypto/smime/bad-digest");
|
|
745
|
+
// v1 requires the signed-attrs path.
|
|
746
|
+
verifyCraft("absent signedAttrs refused",
|
|
747
|
+
{ signedAttrsImplicit: null },
|
|
748
|
+
"mail-crypto/smime/no-signed-attrs");
|
|
749
|
+
// signedAttrs present but no messageDigest attribute.
|
|
750
|
+
verifyCraft("missing messageDigest attribute refused",
|
|
751
|
+
{ signedAttrsImplicit: _signedAttrs([goodCt]).implicit },
|
|
752
|
+
"mail-crypto/smime/no-message-digest-attr");
|
|
753
|
+
// messageDigest matches but the contentType attribute is absent.
|
|
754
|
+
verifyCraft("missing contentType attribute refused",
|
|
755
|
+
{ signedAttrsImplicit: _signedAttrs([goodMd]).implicit },
|
|
756
|
+
"mail-crypto/smime/no-content-type-attr");
|
|
757
|
+
// contentType present but does not equal the eContentType.
|
|
758
|
+
verifyCraft("contentType mismatch refused",
|
|
759
|
+
{ signedAttrsImplicit: _signedAttrs([goodMd, _attr(OID_CT_ATTR, asn1.writeOid("1.2.3.4.7"))]).implicit },
|
|
760
|
+
"mail-crypto/smime/content-type-mismatch");
|
|
761
|
+
// Digest + contentType bind, but the PQC verify itself throws (a signer
|
|
762
|
+
// public key of the wrong length makes ml-dsa verify reject its input).
|
|
763
|
+
verifyCraft("PQC verify throwing surfaces as verify-failed",
|
|
764
|
+
{ signedAttrsImplicit: _signedAttrs([goodMd, goodCt]).implicit, signature: Buffer.from([1, 2, 3]) },
|
|
765
|
+
"mail-crypto/smime/verify-failed", new Uint8Array([1, 2, 3]));
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// _extractMessageDigest walks the attribute SET defensively — a malformed
|
|
769
|
+
// messageDigest attribute is skipped rather than trusted, and the walk then
|
|
770
|
+
// reports the missing attribute. Several distinct malformations exercise the
|
|
771
|
+
// per-attribute skip branches, all resolving to no-message-digest-attr.
|
|
772
|
+
function testSmimeMessageDigestExtractorDefensive() {
|
|
773
|
+
var msg = Buffer.from("md-extractor-body");
|
|
774
|
+
var dummyPub = new Uint8Array(1952);
|
|
775
|
+
var badInner = Buffer.from([0x30, 0x05, 0x01]); // SEQUENCE claiming length 5 with 1 byte
|
|
776
|
+
// Re-tag a raw universal-SET buffer as the SignerInfo's [0] IMPLICIT signedAttrs.
|
|
777
|
+
function implicitFromSet(setBuf) { return Buffer.concat([Buffer.from([0xa0]), setBuf.slice(1)]); }
|
|
778
|
+
function expectNoMd(label, implicit) {
|
|
779
|
+
var env = _craftSignedData({ signedAttrsImplicit: implicit });
|
|
780
|
+
var threw = null;
|
|
781
|
+
try { smime.verify({ message: msg, signature: env, signerPublicKey: dummyPub }); } catch (e) { threw = e; }
|
|
782
|
+
check("md extractor: " + label, threw && threw.code === "mail-crypto/smime/no-message-digest-attr");
|
|
783
|
+
}
|
|
784
|
+
function fromAttrs(attrBufs) { return _signedAttrs(attrBufs).implicit; }
|
|
785
|
+
// messageDigest OID present but its value SET carries an OID, not an OCTET STRING.
|
|
786
|
+
expectNoMd("messageDigest value not an OCTET STRING",
|
|
787
|
+
fromAttrs([_attr(OID_MD_ATTR, asn1.writeOid("1.2.3.4"))]));
|
|
788
|
+
// An attribute SEQUENCE with a single child (attrType only, no values SET).
|
|
789
|
+
expectNoMd("attribute with fewer than two children skipped",
|
|
790
|
+
fromAttrs([asn1.writeSequence([asn1.writeOid(OID_MD_ATTR)]), _attr(OID_CT_ATTR, asn1.writeOid(OID.data))]));
|
|
791
|
+
// messageDigest attrValues is an empty SET.
|
|
792
|
+
expectNoMd("messageDigest with empty values SET skipped",
|
|
793
|
+
fromAttrs([asn1.writeSequence([asn1.writeOid(OID_MD_ATTR), asn1.writeSet([])])]));
|
|
794
|
+
// The signedAttrs SET contents are truncated → the attribute walk throws and
|
|
795
|
+
// the extractor reports the attribute missing.
|
|
796
|
+
expectNoMd("malformed signedAttrs SET body skipped", implicitFromSet(asn1.writeNode(0x31, badInner)));
|
|
797
|
+
// A SET member that is not a SEQUENCE is skipped.
|
|
798
|
+
expectNoMd("non-SEQUENCE attribute skipped", implicitFromSet(asn1.writeSet([asn1.writeOid("1.2.3")])));
|
|
799
|
+
// An attribute SEQUENCE with truncated contents is skipped.
|
|
800
|
+
expectNoMd("attribute with malformed contents skipped",
|
|
801
|
+
implicitFromSet(asn1.writeSet([asn1.writeNode(0x30, badInner)])));
|
|
802
|
+
// An attribute whose attrType is not an OID is skipped.
|
|
803
|
+
expectNoMd("attribute with non-OID attrType skipped",
|
|
804
|
+
implicitFromSet(asn1.writeSet([asn1.writeSequence([asn1.writeSequence([]), asn1.writeSet([])])])));
|
|
805
|
+
// messageDigest attribute present but its values field is not a SET.
|
|
806
|
+
expectNoMd("messageDigest values field not a SET skipped",
|
|
807
|
+
fromAttrs([asn1.writeSequence([asn1.writeOid(OID_MD_ATTR), asn1.writeSequence([])])]));
|
|
808
|
+
// messageDigest attribute present but its values SET body is truncated.
|
|
809
|
+
expectNoMd("messageDigest values SET malformed skipped",
|
|
810
|
+
fromAttrs([asn1.writeSequence([asn1.writeOid(OID_MD_ATTR), asn1.writeNode(0x31, badInner)])]));
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// _audit is drop-silent and tolerates an audit handle that lacks a safeEmit
|
|
814
|
+
// method — sign() must not throw when handed such an object.
|
|
815
|
+
function testSmimeAuditHandleWithoutSafeEmit() {
|
|
816
|
+
var kp = pqc.ml_dsa_65.keygen();
|
|
817
|
+
var out = smime.sign({ message: "audited", certificate: _testCertDer(),
|
|
818
|
+
secretKey: kp.secretKey, sigAlg: "ML-DSA-65", audit: { notSafeEmit: true } });
|
|
819
|
+
check("sign: audit handle without safeEmit is tolerated (drop-silent)",
|
|
820
|
+
typeof out.multipart === "string" && out.multipart.indexOf("multipart/signed") !== -1);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// ---- _extractContentTypeOid(): defensive extraction branches ----
|
|
824
|
+
|
|
825
|
+
function testSmimeContentTypeExtractorDefensive() {
|
|
826
|
+
function isNull(label, raw) {
|
|
827
|
+
check("ct extractor: " + label, smime._extractContentTypeOid(raw) === null);
|
|
828
|
+
}
|
|
829
|
+
var badInner = Buffer.from([0x30, 0x05, 0x01]); // SEQUENCE claiming length 5 with 1 byte
|
|
830
|
+
// Unparseable top-level bytes → null (the readNode itself throws).
|
|
831
|
+
isNull("unparseable top node yields null", Buffer.from([0x31, 0x05, 0x01]));
|
|
832
|
+
// Not a SET at the top → null.
|
|
833
|
+
isNull("non-SET top node yields null", asn1.writeSequence([asn1.writeOid(OID.data)]));
|
|
834
|
+
// Top SET whose contents are truncated so the attribute walk throws → null.
|
|
835
|
+
isNull("malformed SET contents yield null", asn1.writeNode(0x31, badInner));
|
|
836
|
+
// A SET member that is not a SEQUENCE is skipped → null.
|
|
837
|
+
isNull("non-SEQUENCE attribute skipped", asn1.writeSet([asn1.writeOid("1.2.3")]));
|
|
838
|
+
// An attribute SEQUENCE with truncated contents is skipped → null.
|
|
839
|
+
isNull("attribute with malformed contents skipped", asn1.writeSet([asn1.writeNode(0x30, badInner)]));
|
|
840
|
+
// An attribute whose attrType is not an OID is skipped → null.
|
|
841
|
+
isNull("attribute with non-OID attrType skipped",
|
|
842
|
+
asn1.writeSet([asn1.writeSequence([asn1.writeSequence([]), asn1.writeSet([])])]));
|
|
843
|
+
// A SET whose only attribute is not the contentType OID → null.
|
|
844
|
+
isNull("SET without a contentType attribute yields null",
|
|
845
|
+
asn1.writeSet([_attr(OID_MD_ATTR, asn1.writeOctetString(Buffer.alloc(4)))]));
|
|
846
|
+
// contentType attribute present but its values field is not a SET → null.
|
|
847
|
+
isNull("contentType values field not a SET yields null",
|
|
848
|
+
asn1.writeSet([asn1.writeSequence([asn1.writeOid(OID_CT_ATTR), asn1.writeSequence([])])]));
|
|
849
|
+
// contentType attribute present but its values SET is truncated → null.
|
|
850
|
+
isNull("contentType values SET malformed yields null",
|
|
851
|
+
asn1.writeSet([asn1.writeSequence([asn1.writeOid(OID_CT_ATTR), asn1.writeNode(0x31, badInner)])]));
|
|
852
|
+
// contentType attribute present but its values SET is empty → null.
|
|
853
|
+
isNull("contentType with empty values SET yields null",
|
|
854
|
+
asn1.writeSet([asn1.writeSequence([asn1.writeOid(OID_CT_ATTR), asn1.writeSet([])])]));
|
|
855
|
+
// contentType values SET carries a non-OID value → null.
|
|
856
|
+
isNull("contentType value not an OID yields null",
|
|
857
|
+
asn1.writeSet([_attr(OID_CT_ATTR, asn1.writeOctetString(Buffer.from([1])))]));
|
|
858
|
+
// An attribute SEQUENCE with only one child is skipped → null.
|
|
859
|
+
isNull("attribute with a single child skipped",
|
|
860
|
+
asn1.writeSet([asn1.writeSequence([asn1.writeOid(OID_CT_ATTR)])]));
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// The sha3-256 digest option is a documented alternative to the sha3-512
|
|
864
|
+
// default; it drives the sha3-256 micalg in sign() and the sha3-256 resolver
|
|
865
|
+
// in verify().
|
|
866
|
+
function testSmimeSignVerifySha3_256() {
|
|
867
|
+
var kp = pqc.ml_dsa_65.keygen();
|
|
868
|
+
var msg = "From: a@b\r\nSubject: sha3-256\r\n\r\nbody";
|
|
869
|
+
var out = smime.sign({ message: msg, certificate: _testCertDer(), secretKey: kp.secretKey,
|
|
870
|
+
sigAlg: "ML-DSA-65", digestAlg: "sha3-256" });
|
|
871
|
+
check("sign: sha3-256 option sets micalg=sha3-256", out.micalg === "sha3-256");
|
|
872
|
+
var v = smime.verify({ message: msg, signature: out.signature, signerPublicKey: kp.publicKey });
|
|
873
|
+
check("verify: sha3-256 envelope verifies with digestAlg sha3-256",
|
|
874
|
+
v.valid === true && v.digestAlg === "sha3-256");
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// ---- _certKeyMatches(): signer key longer than the cert SPKI ----
|
|
878
|
+
|
|
879
|
+
function testSmimeCertKeyMatchesLongerSigner() {
|
|
880
|
+
var kp = nodeCrypto.generateKeyPairSync("ec", { namedCurve: "P-256" });
|
|
881
|
+
var spki = Buffer.from(kp.publicKey.export({ format: "der", type: "spki" }));
|
|
882
|
+
var longer = Buffer.concat([spki, Buffer.alloc(16)]); // strictly longer than the SPKI
|
|
883
|
+
check("certKeyMatches: signer key longer than SPKI does not match",
|
|
884
|
+
smime._certKeyMatches({ publicKey: kp.publicKey }, longer) === false);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// ---- verifyAll(): zero signers + SKI serial-hex fallback ----
|
|
888
|
+
|
|
889
|
+
function testVerifyAllNoSigners() {
|
|
890
|
+
var env = _craftSignedData({ signerInfos: [] });
|
|
891
|
+
var threw = null;
|
|
892
|
+
try { smime.verifyAll({ message: "x", signature: env, signerPublicKeys: {} }); }
|
|
893
|
+
catch (e) { threw = e; }
|
|
894
|
+
check("verifyAll: zero-SignerInfo envelope refused with no-signers",
|
|
895
|
+
threw && threw.code === "mail-crypto/smime/no-signers");
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
function testVerifyAllSkiSidFallback() {
|
|
899
|
+
// A SignerInfo whose sid is a [0] IMPLICIT SubjectKeyIdentifier (not an
|
|
900
|
+
// issuerAndSerialNumber SEQUENCE). _extractSerialHex falls back to the raw
|
|
901
|
+
// node value hex; with no key mapped to that hex, verifyAll refuses.
|
|
902
|
+
var skiBytes = Buffer.from([0xde, 0xad, 0xbe, 0xef]);
|
|
903
|
+
var sid = asn1.writeContextImplicit(0, skiBytes); // [0] IMPLICIT OCTET STRING
|
|
904
|
+
var sa = _signedAttrs([_attr(OID_CT_ATTR, asn1.writeOid(OID.data))]);
|
|
905
|
+
var si = asn1.writeSequence([
|
|
906
|
+
asn1.writeInteger(Buffer.from([1])), sid, _algId(OID.sha3_512),
|
|
907
|
+
sa.implicit, _algId(OID.mldsa65), asn1.writeOctetString(Buffer.from([0, 0, 1])),
|
|
908
|
+
]);
|
|
909
|
+
var env = _craftSignedData({ signerInfos: [si] });
|
|
910
|
+
var threw = null;
|
|
911
|
+
try { smime.verifyAll({ message: "x", signature: env, signerPublicKeys: {} }); }
|
|
912
|
+
catch (e) { threw = e; }
|
|
913
|
+
check("verifyAll: SKI-variant sid resolves to raw-hex, then missing-key",
|
|
914
|
+
threw && threw.code === "mail-crypto/smime/missing-key");
|
|
915
|
+
check("verifyAll: missing-key names the SKI-derived serial",
|
|
916
|
+
threw && /deadbeef/.test(String(threw.message || "")));
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
// ---- Trust-chain refusals reachable without ML-DSA certs ----
|
|
920
|
+
|
|
921
|
+
function testSmimeTrustChainNoCerts() {
|
|
922
|
+
// sign() embeds no certificates, so verifying with trust anchors present
|
|
923
|
+
// reaches _verifyTrustChain and fails at the empty-certificate-set guard.
|
|
924
|
+
var kp = pqc.ml_dsa_65.keygen();
|
|
925
|
+
var msg = "no-embedded-certs";
|
|
926
|
+
var out = smime.sign({ message: msg, certificate: _testCertDer(), secretKey: kp.secretKey, sigAlg: "ML-DSA-65" });
|
|
927
|
+
var threw = null;
|
|
928
|
+
try {
|
|
929
|
+
smime.verify({ message: msg, signature: out.signature, signerPublicKey: kp.publicKey,
|
|
930
|
+
trustAnchorCertsPem: ["-----BEGIN CERTIFICATE-----\nunused\n-----END CERTIFICATE-----"] });
|
|
931
|
+
} catch (e) { threw = e; }
|
|
932
|
+
check("trust chain: verify with anchors but no embedded certs refused (no-certs)",
|
|
933
|
+
threw && threw.code === "mail-crypto/smime/no-certs");
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function testSmimeTrustChainBadChainCert() {
|
|
937
|
+
// An unparseable cert in SignedData.certificates fails the chain-cert parse.
|
|
938
|
+
var kp = pqc.ml_dsa_65.keygen();
|
|
939
|
+
var msg = Buffer.from("bad-embedded-cert");
|
|
940
|
+
var fakeDer = asn1.writeSequence([asn1.writeInteger(Buffer.from([1]))]);
|
|
941
|
+
var env = b.cms.encodeSignedData({
|
|
942
|
+
encapContent: msg, digestAlg: "sha3-512", detached: true, certificates: [fakeDer],
|
|
943
|
+
signers: [{ certificate: _testCertDer(), secretKey: kp.secretKey, sigAlg: "ML-DSA-65" }],
|
|
944
|
+
});
|
|
945
|
+
var threw = null;
|
|
946
|
+
try {
|
|
947
|
+
smime.verify({ message: msg, signature: env, signerPublicKey: kp.publicKey,
|
|
948
|
+
trustAnchorCertsPem: ["-----BEGIN CERTIFICATE-----\nunused\n-----END CERTIFICATE-----"] });
|
|
949
|
+
} catch (e) { threw = e; }
|
|
950
|
+
check("trust chain: unparseable embedded cert refused (bad-chain-cert)",
|
|
951
|
+
threw && threw.code === "mail-crypto/smime/bad-chain-cert");
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
function testSmimeTrustChainRealCertRefusals() {
|
|
955
|
+
if (!_opensslAvailable()) {
|
|
956
|
+
check("trust chain real-cert refusals skipped (openssl unavailable)", true);
|
|
957
|
+
return;
|
|
958
|
+
}
|
|
959
|
+
var real = _mintCertMinimal({ alg: "rsa", bits: 2048, hash: "sha256", days: 30, cn: "chain-real.example" });
|
|
960
|
+
if (!real) {
|
|
961
|
+
check("trust chain real-cert refusals skipped (cert mint failed)", true);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
var kp = pqc.ml_dsa_65.keygen();
|
|
965
|
+
var msg = Buffer.from("real-cert-in-chain");
|
|
966
|
+
function envWith() {
|
|
967
|
+
return b.cms.encodeSignedData({
|
|
968
|
+
encapContent: msg, digestAlg: "sha3-512", detached: true, certificates: [real.der],
|
|
969
|
+
signers: [{ certificate: _testCertDer(), secretKey: kp.secretKey, sigAlg: "ML-DSA-65" }],
|
|
970
|
+
});
|
|
971
|
+
}
|
|
972
|
+
function refused(label, trustAnchors, code) {
|
|
973
|
+
var threw = null;
|
|
974
|
+
try {
|
|
975
|
+
smime.verify({ message: msg, signature: envWith(), signerPublicKey: kp.publicKey, trustAnchorCertsPem: trustAnchors });
|
|
976
|
+
} catch (e) { threw = e; }
|
|
977
|
+
check("trust chain: " + label, threw && threw.code === code);
|
|
978
|
+
}
|
|
979
|
+
// The PQC signer key matches no embedded (RSA) cert → signer not in chain.
|
|
980
|
+
refused("PQC signer key absent from embedded certs (signer-not-in-chain)",
|
|
981
|
+
[real.pem], "mail-crypto/smime/signer-not-in-chain");
|
|
982
|
+
// A non-string trust anchor entry is refused at anchor parse.
|
|
983
|
+
refused("non-string trust anchor entry refused (bad-trust-anchor)",
|
|
984
|
+
[123], "mail-crypto/smime/bad-trust-anchor");
|
|
985
|
+
// An unparseable trust anchor PEM is refused at anchor parse.
|
|
986
|
+
refused("unparseable trust anchor PEM refused (bad-trust-anchor)",
|
|
987
|
+
["not a certificate"], "mail-crypto/smime/bad-trust-anchor");
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// ---- Trust-chain walk (ML-DSA certs; openssl-signed CMS) ----
|
|
991
|
+
|
|
992
|
+
function testSmimeTrustChainMlDsaWalk() {
|
|
993
|
+
if (!_mlDsaAvailable()) {
|
|
994
|
+
check("trust chain ML-DSA walk skipped (openssl ML-DSA unavailable)", true);
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
_withMlDsaCa(function (tk) {
|
|
998
|
+
var msg = Buffer.from("ml-dsa-chain-walk-body");
|
|
999
|
+
var leaf = tk.selfSigned("Leaf Signer");
|
|
1000
|
+
var other = tk.selfSigned("Unrelated Root");
|
|
1001
|
+
if (!leaf || !other) {
|
|
1002
|
+
check("trust chain ML-DSA walk skipped (cert mint failed)", true);
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
// Self-signed leaf IS the trust anchor → full walk validates.
|
|
1006
|
+
var v = smime.verify({ message: msg, signature: _mlDsaEnvelope(tk, leaf.keyPath, msg, [leaf.der]),
|
|
1007
|
+
signerPublicKey: leaf.rawPub, trustAnchorCertsPem: [leaf.pem] });
|
|
1008
|
+
check("trust chain: self-signed leaf as anchor verifies with chainVerified",
|
|
1009
|
+
v.valid === true && v.chainVerified === true);
|
|
1010
|
+
|
|
1011
|
+
// Same leaf, but the only anchor is an unrelated root → no anchor reached.
|
|
1012
|
+
var t1 = null;
|
|
1013
|
+
try {
|
|
1014
|
+
smime.verify({ message: msg, signature: _mlDsaEnvelope(tk, leaf.keyPath, msg, [leaf.der]),
|
|
1015
|
+
signerPublicKey: leaf.rawPub, trustAnchorCertsPem: [other.pem] });
|
|
1016
|
+
} catch (e) { t1 = e; }
|
|
1017
|
+
check("trust chain: leaf not reaching any anchor refused (untrusted-chain)",
|
|
1018
|
+
t1 && t1.code === "mail-crypto/smime/untrusted-chain");
|
|
1019
|
+
|
|
1020
|
+
// An expired cert alongside the valid leaf trips the validity-window check.
|
|
1021
|
+
var expired = tk.selfSigned("Expired Cert", { notBefore: "20200101000000Z", notAfter: "20200102000000Z" });
|
|
1022
|
+
if (expired) {
|
|
1023
|
+
var t2 = null;
|
|
1024
|
+
try {
|
|
1025
|
+
smime.verify({ message: msg, signature: _mlDsaEnvelope(tk, leaf.keyPath, msg, [leaf.der, expired.der]),
|
|
1026
|
+
signerPublicKey: leaf.rawPub, trustAnchorCertsPem: [leaf.pem] });
|
|
1027
|
+
} catch (e) { t2 = e; }
|
|
1028
|
+
check("trust chain: expired chain cert refused (cert-expired)",
|
|
1029
|
+
t2 && t2.code === "mail-crypto/smime/cert-expired");
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// A not-yet-valid cert alongside the valid leaf trips the same check.
|
|
1033
|
+
var future = tk.selfSigned("Future Cert", { notBefore: "20350101000000Z", notAfter: "20360101000000Z" });
|
|
1034
|
+
if (future) {
|
|
1035
|
+
var t3 = null;
|
|
1036
|
+
try {
|
|
1037
|
+
smime.verify({ message: msg, signature: _mlDsaEnvelope(tk, leaf.keyPath, msg, [leaf.der, future.der]),
|
|
1038
|
+
signerPublicKey: leaf.rawPub, trustAnchorCertsPem: [leaf.pem] });
|
|
1039
|
+
} catch (e) { t3 = e; }
|
|
1040
|
+
check("trust chain: not-yet-valid chain cert refused (cert-not-yet-valid)",
|
|
1041
|
+
t3 && t3.code === "mail-crypto/smime/cert-not-yet-valid");
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
function testSmimeTrustChainMlDsaIntermediate() {
|
|
1047
|
+
if (!_mlDsaAvailable()) {
|
|
1048
|
+
check("trust chain ML-DSA intermediate skipped (openssl ML-DSA unavailable)", true);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
_withMlDsaCa(function (tk) {
|
|
1052
|
+
var root = tk.selfSigned("Root CA");
|
|
1053
|
+
if (!root) { check("trust chain intermediate skipped (root mint failed)", true); return; }
|
|
1054
|
+
var inter = tk.caSign("Intermediate CA", root);
|
|
1055
|
+
var leaf = inter && tk.caSign("Leaf Signer", inter);
|
|
1056
|
+
if (!inter || !leaf) { check("trust chain intermediate skipped (CA issuance failed)", true); return; }
|
|
1057
|
+
var msg = Buffer.from("intermediate-walk-body");
|
|
1058
|
+
// Chain carries leaf + intermediate; only the root is a trust anchor, so
|
|
1059
|
+
// the walk must hop leaf → intermediate → root.
|
|
1060
|
+
var v = smime.verify({ message: msg, signature: _mlDsaEnvelope(tk, leaf.keyPath, msg, [leaf.der, inter.der]),
|
|
1061
|
+
signerPublicKey: leaf.rawPub, trustAnchorCertsPem: [root.pem] });
|
|
1062
|
+
check("trust chain: leaf → intermediate → root walk validates",
|
|
1063
|
+
v.valid === true && v.chainVerified === true);
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
function testVerifyAllTrustChain() {
|
|
1068
|
+
if (!_mlDsaAvailable()) {
|
|
1069
|
+
check("verifyAll trust chain skipped (openssl ML-DSA unavailable)", true);
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
_withMlDsaCa(function (tk) {
|
|
1073
|
+
var leaf = tk.selfSigned("VerifyAll Leaf");
|
|
1074
|
+
if (!leaf) { check("verifyAll trust chain skipped (cert mint failed)", true); return; }
|
|
1075
|
+
var msg = Buffer.from("verifyall-chain-body");
|
|
1076
|
+
var v = smime.verifyAll({ message: msg, signature: _mlDsaEnvelope(tk, leaf.keyPath, msg, [leaf.der]),
|
|
1077
|
+
signerPublicKeys: { "01": leaf.rawPub }, trustAnchorCertsPem: [leaf.pem] });
|
|
1078
|
+
check("verifyAll: trust-anchor chain validates through the bundle",
|
|
1079
|
+
v.valid === true && v.chainVerified === true);
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// ---- checkCert(): parsed-cert body (real openssl certs) ----
|
|
1084
|
+
|
|
1085
|
+
function testSmimeCheckCertRealCerts() {
|
|
1086
|
+
if (!_opensslAvailable()) {
|
|
1087
|
+
check("checkCert real-cert body skipped (openssl unavailable)", true);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
var rsa = _mintCertMinimal({ alg: "rsa", bits: 2048, hash: "sha256", days: 30, cn: "rsa.example" });
|
|
1091
|
+
if (rsa) {
|
|
1092
|
+
var rv = smime.checkCert({ certPem: rsa.pem });
|
|
1093
|
+
check("checkCert: RSA-2048/sha256 cert returns rsa keyType", rv.keyType === "rsa");
|
|
1094
|
+
check("checkCert: reports the signature algorithm name", /rsa/i.test(rv.sigAlgName));
|
|
1095
|
+
check("checkCert: returns a SHA-256 fingerprint", /^([0-9A-F]{2}:){31}[0-9A-F]{2}$/.test(rv.fingerprint256));
|
|
1096
|
+
check("checkCert: returns subject + issuer DN strings",
|
|
1097
|
+
typeof rv.subject === "string" && typeof rv.issuer === "string");
|
|
1098
|
+
} else {
|
|
1099
|
+
check("checkCert RSA happy path skipped (mint failed)", true);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// EC cert exercises the non-RSA path — the RSA bit-floor block is skipped.
|
|
1103
|
+
var ec = _mintCertMinimal({ alg: "ec", curve: "P-256", hash: "sha256", days: 30, cn: "ec.example" });
|
|
1104
|
+
if (ec) {
|
|
1105
|
+
var ev = smime.checkCert({ certPem: ec.pem });
|
|
1106
|
+
check("checkCert: EC cert reports ec keyType (RSA floor skipped)", ev.keyType === "ec");
|
|
1107
|
+
} else {
|
|
1108
|
+
check("checkCert EC happy path skipped (mint failed)", true);
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// SHA-1-signed cert is refused at the signature-algorithm screen.
|
|
1112
|
+
var sha1 = _mintCertMinimal({ alg: "rsa", bits: 2048, hash: "sha1", days: 30, cn: "sha1.example" });
|
|
1113
|
+
if (sha1) {
|
|
1114
|
+
var t1 = null;
|
|
1115
|
+
try { smime.checkCert({ certPem: sha1.pem }); } catch (e) { t1 = e; }
|
|
1116
|
+
check("checkCert: SHA-1 cert signature refused (refused-hash)",
|
|
1117
|
+
t1 && t1.code === "mail-crypto/smime/refused-hash");
|
|
1118
|
+
} else {
|
|
1119
|
+
check("checkCert SHA-1 refusal skipped (mint failed)", true);
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// Sub-2048-bit RSA is refused at the bit floor.
|
|
1123
|
+
var weak = _mintCertMinimal({ alg: "rsa", bits: 1024, hash: "sha256", days: 30, cn: "weak.example" });
|
|
1124
|
+
if (weak) {
|
|
1125
|
+
var t2 = null;
|
|
1126
|
+
try { smime.checkCert({ certPem: weak.pem }); } catch (e) { t2 = e; }
|
|
1127
|
+
check("checkCert: RSA-1024 cert refused at the bit floor (rsa-too-small)",
|
|
1128
|
+
t2 && t2.code === "mail-crypto/smime/rsa-too-small");
|
|
1129
|
+
} else {
|
|
1130
|
+
check("checkCert small-RSA refusal skipped (mint failed)", true);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
function testSmimeCheckCertValidityWindow() {
|
|
1135
|
+
if (!_opensslAvailable()) {
|
|
1136
|
+
check("checkCert validity window skipped (openssl unavailable)", true);
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
var expired = _mintCertMinimal({ alg: "rsa", bits: 2048, hash: "sha256",
|
|
1140
|
+
notBefore: "20200101000000Z", notAfter: "20200102000000Z", cn: "expired.example" });
|
|
1141
|
+
if (expired) {
|
|
1142
|
+
var t1 = null;
|
|
1143
|
+
try { smime.checkCert({ certPem: expired.pem }); } catch (e) { t1 = e; }
|
|
1144
|
+
check("checkCert: expired cert refused (expired-cert)",
|
|
1145
|
+
t1 && t1.code === "mail-crypto/smime/expired-cert" && /expired/.test(t1.message));
|
|
1146
|
+
} else {
|
|
1147
|
+
check("checkCert expired-cert skipped (mint failed)", true);
|
|
1148
|
+
}
|
|
1149
|
+
var future = _mintCertMinimal({ alg: "rsa", bits: 2048, hash: "sha256",
|
|
1150
|
+
notBefore: "20350101000000Z", notAfter: "20360101000000Z", cn: "future.example" });
|
|
1151
|
+
if (future) {
|
|
1152
|
+
var t2 = null;
|
|
1153
|
+
try { smime.checkCert({ certPem: future.pem }); } catch (e) { t2 = e; }
|
|
1154
|
+
check("checkCert: not-yet-valid cert refused (expired-cert / not yet valid)",
|
|
1155
|
+
t2 && t2.code === "mail-crypto/smime/expired-cert" && /not yet valid/.test(t2.message));
|
|
1156
|
+
} else {
|
|
1157
|
+
check("checkCert not-yet-valid skipped (mint failed)", true);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
|
|
323
1161
|
function run() {
|
|
324
1162
|
testSmimeSurface();
|
|
325
1163
|
testSmimeContentTypeAttrExtraction();
|
|
@@ -335,6 +1173,32 @@ function run() {
|
|
|
335
1173
|
testSmimeCheckCertRefusesSmallRsa();
|
|
336
1174
|
testSmimeCheckCertInputValidation();
|
|
337
1175
|
testSmimeDocBlockCitations();
|
|
1176
|
+
testVerifyAllMultiSigner();
|
|
1177
|
+
testVerifyAllPerSignerKeyBinding();
|
|
1178
|
+
testVerifyAllMissingKey();
|
|
1179
|
+
testVerifyAllTamperRefused();
|
|
1180
|
+
testVerifyAllSingleSigner();
|
|
1181
|
+
testVerifyAllInputValidation();
|
|
1182
|
+
// Uncovered error / adversarial / defensive branches.
|
|
1183
|
+
testSmimeSignInputValidation();
|
|
1184
|
+
testSmimeVerifyInputValidation();
|
|
1185
|
+
testSmimeVerifyNoSigners();
|
|
1186
|
+
testSmimeVerifySignerInfoRefusals();
|
|
1187
|
+
testSmimeMessageDigestExtractorDefensive();
|
|
1188
|
+
testSmimeContentTypeExtractorDefensive();
|
|
1189
|
+
testSmimeAuditHandleWithoutSafeEmit();
|
|
1190
|
+
testSmimeSignVerifySha3_256();
|
|
1191
|
+
testSmimeCertKeyMatchesLongerSigner();
|
|
1192
|
+
testVerifyAllNoSigners();
|
|
1193
|
+
testVerifyAllSkiSidFallback();
|
|
1194
|
+
testSmimeTrustChainNoCerts();
|
|
1195
|
+
testSmimeTrustChainBadChainCert();
|
|
1196
|
+
testSmimeTrustChainRealCertRefusals();
|
|
1197
|
+
testSmimeTrustChainMlDsaWalk();
|
|
1198
|
+
testSmimeTrustChainMlDsaIntermediate();
|
|
1199
|
+
testVerifyAllTrustChain();
|
|
1200
|
+
testSmimeCheckCertRealCerts();
|
|
1201
|
+
testSmimeCheckCertValidityWindow();
|
|
338
1202
|
}
|
|
339
1203
|
|
|
340
1204
|
module.exports = { run: run };
|