@blamejs/blamejs-shop 0.1.32 → 0.1.34
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 +4 -0
- package/README.md +2 -0
- package/lib/admin.js +18 -10
- package/lib/asset-manifest.json +17 -5
- package/lib/storefront.js +326 -44
- package/lib/vendor/MANIFEST.json +2 -2
- package/lib/vendor/blamejs/CHANGELOG.md +10 -0
- package/lib/vendor/blamejs/README.md +4 -2
- package/lib/vendor/blamejs/api-snapshot.json +107 -2
- package/lib/vendor/blamejs/index.js +3 -0
- package/lib/vendor/blamejs/lib/crypto-oprf.js +110 -0
- package/lib/vendor/blamejs/lib/iab-tcf.js +277 -2
- package/lib/vendor/blamejs/lib/network-tsig.js +404 -0
- package/lib/vendor/blamejs/lib/network.js +1 -0
- package/lib/vendor/blamejs/lib/vendor/MANIFEST.json +44 -9
- package/lib/vendor/blamejs/lib/vendor/noble-curves.cjs +19 -0
- package/lib/vendor/blamejs/lib/worm.js +246 -0
- package/lib/vendor/blamejs/package.json +1 -1
- package/lib/vendor/blamejs/release-notes/v0.12.x.json +1844 -0
- package/lib/vendor/blamejs/release-notes/v0.13.0.json +22 -0
- package/lib/vendor/blamejs/release-notes/v0.13.1.json +18 -0
- package/lib/vendor/blamejs/release-notes/v0.13.2.json +27 -0
- package/lib/vendor/blamejs/scripts/vendor-update.sh +11 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/codebase-patterns.test.js +3 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/crypto-oprf.test.js +101 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/iab-tcf.test.js +61 -1
- package/lib/vendor/blamejs/test/layer-0-primitives/network-tsig.test.js +149 -0
- package/lib/vendor/blamejs/test/layer-0-primitives/sandbox.test.js +2 -2
- package/lib/vendor/blamejs/test/layer-0-primitives/testing.test.js +3 -3
- package/lib/vendor/blamejs/test/layer-0-primitives/worm.test.js +126 -0
- package/package.json +2 -2
- package/lib/vendor/blamejs/release-notes/v0.12.0.json +0 -64
- package/lib/vendor/blamejs/release-notes/v0.12.1.json +0 -32
- package/lib/vendor/blamejs/release-notes/v0.12.10.json +0 -65
- package/lib/vendor/blamejs/release-notes/v0.12.11.json +0 -39
- package/lib/vendor/blamejs/release-notes/v0.12.12.json +0 -48
- package/lib/vendor/blamejs/release-notes/v0.12.13.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.12.14.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.15.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.16.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.17.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.18.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.19.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.2.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.12.20.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.21.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.22.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.23.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.24.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.25.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.26.json +0 -30
- package/lib/vendor/blamejs/release-notes/v0.12.27.json +0 -26
- package/lib/vendor/blamejs/release-notes/v0.12.28.json +0 -26
- package/lib/vendor/blamejs/release-notes/v0.12.29.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.12.3.json +0 -23
- package/lib/vendor/blamejs/release-notes/v0.12.30.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.31.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.32.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.33.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.12.34.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.35.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.36.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.37.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.38.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.39.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.4.json +0 -19
- package/lib/vendor/blamejs/release-notes/v0.12.40.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.41.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.42.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.43.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.44.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.45.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.46.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.47.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.48.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.49.json +0 -31
- package/lib/vendor/blamejs/release-notes/v0.12.5.json +0 -40
- package/lib/vendor/blamejs/release-notes/v0.12.50.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.51.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.52.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.53.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.54.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.55.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.56.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.57.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.58.json +0 -22
- package/lib/vendor/blamejs/release-notes/v0.12.6.json +0 -45
- package/lib/vendor/blamejs/release-notes/v0.12.60.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.61.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.62.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.63.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.64.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.65.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.66.json +0 -18
- package/lib/vendor/blamejs/release-notes/v0.12.68.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.69.json +0 -27
- package/lib/vendor/blamejs/release-notes/v0.12.7.json +0 -86
- package/lib/vendor/blamejs/release-notes/v0.12.8.json +0 -81
- package/lib/vendor/blamejs/release-notes/v0.12.9.json +0 -61
|
@@ -89,8 +89,10 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
|
|
|
89
89
|
- **Workflow gates** — break-glass column gates with second-factor + audit (`b.breakGlass`); two-person-rule m-of-n approval with cooling-off lock + cancellation (`b.dualControl`)
|
|
90
90
|
- **Financial / Open Banking** — FAPI 2.0 Final composite posture (PAR + PKCE-S256 + DPoP-or-mTLS + RFC 9207); runtime enforcement helpers `b.fapi2.assertCallback` (refuses missing iss + bare-param under message-signing) and `b.fapi2.assertAuthzRequest` (refuses non-JAR); CFPB §1033 / FDX 6.0 consumer-financial-data-sharing wrapper (`b.fdx`)
|
|
91
91
|
- **Data-subject coordination** — cross-table export / rectify / erase / restrict / objection (`b.subject`, `b.subject.eraseHard`); subject-level legal-hold registry consulted by erase + retention paths (FRCP Rule 26/37(e), GDPR Art 17(3)(e), SEC Rule 17a-4, HIPAA §164.530(j)(2)) (`b.legalHold`)
|
|
92
|
+
- **WORM retention** — write-once-read-many records over any backing store (`b.worm.create`): `compliance` / `governance` Object-Lock modes, extend-only `retainUntil`, legal holds, and a tamper-evident SHA3-512 digest verified on read — the store-agnostic application-level companion to `b.objectStore`'s S3 Object Lock, for sealed-DB / filesystem / non-S3 backends (SEC 17a-4(f), CFTC 1.31, FINRA 4511)
|
|
92
93
|
- **Account safety** — adaptive bot-challenge staircase (`b.authBotChallenge`); session-to-device-posture binding with fail-closed verify (`b.sessionDeviceBinding`)
|
|
93
94
|
- **Anonymous authorization** — Privacy Pass origin side (RFC 9577/9578 — `b.privacyPass`): issue a `WWW-Authenticate: PrivateToken` challenge and verify a presented Blind-RSA (type 0x0002) token against the issuer public key, with no issuer callback and no client identity
|
|
95
|
+
- **Oblivious PRF** — RFC 9497 OPRF / VOPRF (`b.crypto.oprf.suite`): learn `F(serverKey, input)` without the server seeing the input — the primitive behind password hardening (pepper a password the server never sees), private set intersection, and Privacy Pass; `oprf` (base) + `voprf` (verifiable, DLEQ-proof) modes over ristretto255-SHA512 / P-256 / P-384 / P-521; validated against the RFC 9497 Appendix-A vectors
|
|
94
96
|
### Crypto
|
|
95
97
|
|
|
96
98
|
- **At-rest envelope** — envelope-versioned PQC (ML-KEM-1024 + P-384 hybrid, XChaCha20-Poly1305, SHAKE256); vault sealing (`b.crypto`, `b.vault`)
|
|
@@ -131,7 +133,7 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
|
|
|
131
133
|
- In-process CIDR fence (`b.middleware.networkAllowlist`)
|
|
132
134
|
- `Cache-Control: no-store` on every 401 from `requireAuth` / `requireAal` / `requireStepUp` per RFC 9111 §5.2.2.5
|
|
133
135
|
- **Outbound HTTP client** — HTTP/1.1 + HTTP/2 with SSRF gate (cloud-metadata IPs hard-denied; private / loopback / link-local overridable per call); scheme + userinfo + per-host destination allowlist; redirects, multipart, interceptors, progress, encrypted cookie jar (`b.httpClient`, `b.ssrfGuard`, `b.safeUrl`)
|
|
134
|
-
- **Network configurability (`b.network`)** — env-driven NTP / NTS (RFC 8915), IPv4/IPv6 NTP, DNS with IPv6 / DoH / DoT (private-CA pinning) / cache / lookup timeout; local DNSSEC signature verification (RFC 4035 — `b.network.dns.dnssec.verifyRrset` over a canonicalised RRset against RSA / ECDSA P-256·P-384 / Ed25519 DNSKEYs, plus DS-digest + key-tag, plus `verifyDenial` for NSEC / NSEC3 (RFC 5155) NXDOMAIN / NODATA proofs with iteration caps + Opt-Out handling, plus `verifyChain` to validate a full root→TLD→zone delegation chain against the pinned IANA root anchors) so a resolver client can verify both positive and negative answers instead of trusting the upstream AD bit; DANE / TLSA certificate matching (RFC 6698/7671 — `b.network.dns.dane.matchCertificate`) to pin a service's key through DNSSEC instead of a public CA; outbound HTTP proxy (`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`); runtime DPI trust-store CA additions; application-level heartbeats; TCP socket defaults
|
|
136
|
+
- **Network configurability (`b.network`)** — env-driven NTP / NTS (RFC 8915), IPv4/IPv6 NTP, DNS with IPv6 / DoH / DoT (private-CA pinning) / cache / lookup timeout; local DNSSEC signature verification (RFC 4035 — `b.network.dns.dnssec.verifyRrset` over a canonicalised RRset against RSA / ECDSA P-256·P-384 / Ed25519 DNSKEYs, plus DS-digest + key-tag, plus `verifyDenial` for NSEC / NSEC3 (RFC 5155) NXDOMAIN / NODATA proofs with iteration caps + Opt-Out handling, plus `verifyChain` to validate a full root→TLD→zone delegation chain against the pinned IANA root anchors) so a resolver client can verify both positive and negative answers instead of trusting the upstream AD bit; DANE / TLSA certificate matching (RFC 6698/7671 — `b.network.dns.dane.matchCertificate`) to pin a service's key through DNSSEC instead of a public CA; TSIG transaction signatures (RFC 8945 — `b.network.dns.tsig.sign` / `verify`) for shared-key HMAC authentication of zone transfers, dynamic updates, and query/response pairs, with constant-time MAC compare + fudge-window check (verified against dnspython); outbound HTTP proxy (`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`); runtime DPI trust-store CA additions; application-level heartbeats; TCP socket defaults
|
|
135
137
|
- **Error pages** — operator-rendered, no app-frame leakage (`b.errorPage`)
|
|
136
138
|
### Defensive parsers
|
|
137
139
|
|
|
@@ -205,7 +207,7 @@ The framework bundles the surface a typical Node app reaches for. Every primitiv
|
|
|
205
207
|
- **Change control + WORM** — m-of-n approver DDL change-control with maintenance-window + ML-DSA-87 signed proposals (`b.ddlChangeControl`); row-level WORM triggers boot-asserted under `sec-17a-4` / `finra-4511` / `fda-21cfr11` (`b.db.declareWorm`); dual-control physical delete + crypto-erase + REINDEX in one transaction (`b.db.declareRequireDualControl`, `b.db.eraseHard`)
|
|
206
208
|
- **Consumer-protection** — FTC click-to-cancel UX-parity attestation (`ftc-2024` / `ca-sb942` / `strict`) (`b.darkPatterns`)
|
|
207
209
|
- **Differential privacy** — float-safe DP for aggregate releases: snapping-mechanism Laplace (Mironov 2012) + discrete Gaussian (Canonne–Kamath–Steinke 2020), CSPRNG noise, per-scope ε/δ budgets with basic + Rényi-DP accounting; defends the floating-point distinguishing attack that breaks naive Laplace samplers (NIST SP 800-226) (`b.ai.dp`)
|
|
208
|
-
- **Privacy / DSR** — GDPR Articles 15–22 / CCPA / CPRA / LGPD / PIPEDA data-subject-rights workflow (`b.dsr`); IAB TCF v2
|
|
210
|
+
- **Privacy / DSR** — GDPR Articles 15–22 / CCPA / CPRA / LGPD / PIPEDA data-subject-rights workflow (`b.dsr`); IAB TCF v2 consent-string parse + encode + `disclosedVendors` validator (`b.iabTcf`); IAB MSPA / GPP universal-opt-out (USNAT / USCA / USVA / USCO / USCT / USUT) + GPC mirror (`b.iabMspa`); generic consent capture + withdrawal (`b.consent`)
|
|
209
211
|
- **Incident reporters** — EU DORA Article 17 ICT-incident workflow per Commission Delegated Regulation 2024/1772 (`b.dora`); EU NIS2 (`b.nis2`); EU Cyber Resilience Act SBOM + secure-software-attestation (`b.cra`); SEC Form 8-K Item 1.05 cybersecurity-incident materiality-disclosure (`b.secCyber`); incident lifecycle coordinator (`b.incident`)
|
|
210
212
|
- **Outbound DLP** — interceptor-installed on httpClient + mail + webhook with built-in detectors for PAN (Luhn), SSN, EIN, IBAN (mod-97), api-key shapes, PEM, SSH private keys, JWTs, AWS access keys, PHI composite; refuse / redact / audit-only verdicts under pci-dss / hipaa / fapi2 / soc2 / gdpr presets (`b.redact.installOutboundDlp`)
|
|
211
213
|
### Observability
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 1,
|
|
3
|
-
"frameworkVersion": "0.
|
|
4
|
-
"createdAt": "2026-05-
|
|
3
|
+
"frameworkVersion": "0.13.2",
|
|
4
|
+
"createdAt": "2026-05-26T22:29:54.847Z",
|
|
5
5
|
"exports": {
|
|
6
6
|
"a2a": {
|
|
7
7
|
"type": "object",
|
|
@@ -13951,6 +13951,23 @@
|
|
|
13951
13951
|
"type": "function",
|
|
13952
13952
|
"arity": 3
|
|
13953
13953
|
},
|
|
13954
|
+
"oprf": {
|
|
13955
|
+
"type": "object",
|
|
13956
|
+
"members": {
|
|
13957
|
+
"OprfError": {
|
|
13958
|
+
"type": "function",
|
|
13959
|
+
"arity": 4
|
|
13960
|
+
},
|
|
13961
|
+
"SUITES": {
|
|
13962
|
+
"type": "instance",
|
|
13963
|
+
"ctorName": "Array"
|
|
13964
|
+
},
|
|
13965
|
+
"suite": {
|
|
13966
|
+
"type": "function",
|
|
13967
|
+
"arity": 1
|
|
13968
|
+
}
|
|
13969
|
+
}
|
|
13970
|
+
},
|
|
13954
13971
|
"randomInt": {
|
|
13955
13972
|
"type": "function",
|
|
13956
13973
|
"arity": 2
|
|
@@ -36811,6 +36828,14 @@
|
|
|
36811
36828
|
"type": "function",
|
|
36812
36829
|
"arity": 2
|
|
36813
36830
|
},
|
|
36831
|
+
"encode": {
|
|
36832
|
+
"type": "function",
|
|
36833
|
+
"arity": 1
|
|
36834
|
+
},
|
|
36835
|
+
"isValid": {
|
|
36836
|
+
"type": "function",
|
|
36837
|
+
"arity": 1
|
|
36838
|
+
},
|
|
36814
36839
|
"parseString": {
|
|
36815
36840
|
"type": "function",
|
|
36816
36841
|
"arity": 1
|
|
@@ -42208,6 +42233,69 @@
|
|
|
42208
42233
|
"type": "function",
|
|
42209
42234
|
"arity": 1
|
|
42210
42235
|
},
|
|
42236
|
+
"tsig": {
|
|
42237
|
+
"type": "object",
|
|
42238
|
+
"members": {
|
|
42239
|
+
"ALGORITHMS": {
|
|
42240
|
+
"type": "object",
|
|
42241
|
+
"members": {
|
|
42242
|
+
"hmac-sha224": {
|
|
42243
|
+
"type": "primitive",
|
|
42244
|
+
"valueType": "string"
|
|
42245
|
+
},
|
|
42246
|
+
"hmac-sha256": {
|
|
42247
|
+
"type": "primitive",
|
|
42248
|
+
"valueType": "string"
|
|
42249
|
+
},
|
|
42250
|
+
"hmac-sha384": {
|
|
42251
|
+
"type": "primitive",
|
|
42252
|
+
"valueType": "string"
|
|
42253
|
+
},
|
|
42254
|
+
"hmac-sha512": {
|
|
42255
|
+
"type": "primitive",
|
|
42256
|
+
"valueType": "string"
|
|
42257
|
+
}
|
|
42258
|
+
}
|
|
42259
|
+
},
|
|
42260
|
+
"ERROR": {
|
|
42261
|
+
"type": "object",
|
|
42262
|
+
"members": {
|
|
42263
|
+
"BADKEY": {
|
|
42264
|
+
"type": "primitive",
|
|
42265
|
+
"valueType": "number"
|
|
42266
|
+
},
|
|
42267
|
+
"BADSIG": {
|
|
42268
|
+
"type": "primitive",
|
|
42269
|
+
"valueType": "number"
|
|
42270
|
+
},
|
|
42271
|
+
"BADTIME": {
|
|
42272
|
+
"type": "primitive",
|
|
42273
|
+
"valueType": "number"
|
|
42274
|
+
},
|
|
42275
|
+
"BADTRUNC": {
|
|
42276
|
+
"type": "primitive",
|
|
42277
|
+
"valueType": "number"
|
|
42278
|
+
},
|
|
42279
|
+
"NOERROR": {
|
|
42280
|
+
"type": "primitive",
|
|
42281
|
+
"valueType": "number"
|
|
42282
|
+
}
|
|
42283
|
+
}
|
|
42284
|
+
},
|
|
42285
|
+
"TsigError": {
|
|
42286
|
+
"type": "function",
|
|
42287
|
+
"arity": 4
|
|
42288
|
+
},
|
|
42289
|
+
"sign": {
|
|
42290
|
+
"type": "function",
|
|
42291
|
+
"arity": 2
|
|
42292
|
+
},
|
|
42293
|
+
"verify": {
|
|
42294
|
+
"type": "function",
|
|
42295
|
+
"arity": 2
|
|
42296
|
+
}
|
|
42297
|
+
}
|
|
42298
|
+
},
|
|
42211
42299
|
"useDesignatedResolvers": {
|
|
42212
42300
|
"type": "function",
|
|
42213
42301
|
"arity": 1
|
|
@@ -50270,6 +50358,23 @@
|
|
|
50270
50358
|
}
|
|
50271
50359
|
}
|
|
50272
50360
|
},
|
|
50361
|
+
"worm": {
|
|
50362
|
+
"type": "object",
|
|
50363
|
+
"members": {
|
|
50364
|
+
"MODES": {
|
|
50365
|
+
"type": "instance",
|
|
50366
|
+
"ctorName": "Array"
|
|
50367
|
+
},
|
|
50368
|
+
"WormError": {
|
|
50369
|
+
"type": "function",
|
|
50370
|
+
"arity": 4
|
|
50371
|
+
},
|
|
50372
|
+
"create": {
|
|
50373
|
+
"type": "function",
|
|
50374
|
+
"arity": 1
|
|
50375
|
+
}
|
|
50376
|
+
}
|
|
50377
|
+
},
|
|
50273
50378
|
"wsClient": {
|
|
50274
50379
|
"type": "object",
|
|
50275
50380
|
"members": {
|
|
@@ -57,6 +57,7 @@ var crypto = require("./lib/crypto");
|
|
|
57
57
|
// remembering separate top-level namespaces. Implementations live in
|
|
58
58
|
// the dedicated lib files; these are thin aliases.
|
|
59
59
|
crypto.hpke = require("./lib/crypto-hpke");
|
|
60
|
+
crypto.oprf = require("./lib/crypto-oprf");
|
|
60
61
|
// Both PQ-HPKE drafts behind one opt-in sub-namespace — see
|
|
61
62
|
// lib/crypto-hpke-pq.js. Operators that need a draft-codepoint
|
|
62
63
|
// shape reach for b.crypto.hpke.pq.connolly / .wg explicitly; the
|
|
@@ -371,6 +372,7 @@ var fileUpload = require("./lib/file-upload");
|
|
|
371
372
|
var dualControl = require("./lib/dual-control");
|
|
372
373
|
var retention = require("./lib/retention");
|
|
373
374
|
var legalHold = require("./lib/legal-hold");
|
|
375
|
+
var worm = require("./lib/worm");
|
|
374
376
|
var network = require("./lib/network");
|
|
375
377
|
var cloudEvents = require("./lib/cloud-events");
|
|
376
378
|
var dsr = require("./lib/dsr");
|
|
@@ -709,6 +711,7 @@ module.exports = {
|
|
|
709
711
|
dualControl: dualControl,
|
|
710
712
|
retention: retention,
|
|
711
713
|
legalHold: legalHold,
|
|
714
|
+
worm: worm,
|
|
712
715
|
network: network,
|
|
713
716
|
cloudEvents: cloudEvents,
|
|
714
717
|
dsr: dsr,
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @module b.crypto.oprf
|
|
4
|
+
* @nav Crypto
|
|
5
|
+
* @title OPRF
|
|
6
|
+
*
|
|
7
|
+
* @intro
|
|
8
|
+
* Oblivious Pseudorandom Functions per <a
|
|
9
|
+
* href="https://www.rfc-editor.org/rfc/rfc9497">RFC 9497</a>. An OPRF lets
|
|
10
|
+
* a client learn <code>F(serverKey, input)</code> — a keyed pseudorandom
|
|
11
|
+
* value — <em>without</em> the server learning the input and without the
|
|
12
|
+
* client learning the key. It is the primitive behind Privacy Pass
|
|
13
|
+
* tokens, password-breach checks and password hardening (the server can
|
|
14
|
+
* pepper a password without ever seeing it), and private set
|
|
15
|
+
* intersection.
|
|
16
|
+
*
|
|
17
|
+
* Two modes are provided per RFC 9497: <code>oprf</code> (base) and
|
|
18
|
+
* <code>voprf</code> (verifiable — the client can prove the server used
|
|
19
|
+
* the committed key, via a DLEQ proof carried in the evaluation). The
|
|
20
|
+
* partially-oblivious <code>poprf</code> mode is not yet exposed: the
|
|
21
|
+
* vendored <code>@noble/curves</code> does not implement it, so it will
|
|
22
|
+
* be added when upstream ships it rather than stubbed here.
|
|
23
|
+
* The base protocol is: the client <code>blind</code>s its input to a
|
|
24
|
+
* group element, the server <code>blindEvaluate</code>s it with its
|
|
25
|
+
* secret key, and the client <code>finalize</code>s by un-blinding and
|
|
26
|
+
* hashing. Because un-blinding cancels the blind, the output depends only
|
|
27
|
+
* on the key and the input — a server-side <code>evaluate</code> produces
|
|
28
|
+
* the same value directly.
|
|
29
|
+
*
|
|
30
|
+
* <code>suite(name)</code> returns the suite for one of the RFC 9497
|
|
31
|
+
* ciphersuites — <code>ristretto255-sha512</code> (the Privacy Pass
|
|
32
|
+
* default), <code>p256-sha256</code>, <code>p384-sha384</code>, or
|
|
33
|
+
* <code>p521-sha512</code> — each exposing the three modes. Group and
|
|
34
|
+
* hash-to-curve operations come from the vendored <code>@noble/curves</code>.
|
|
35
|
+
* Byte arguments are <code>Uint8Array</code> / <code>Buffer</code>;
|
|
36
|
+
* returned elements and outputs are <code>Uint8Array</code>.
|
|
37
|
+
*
|
|
38
|
+
* @card
|
|
39
|
+
* RFC 9497 Oblivious PRFs — learn <code>F(key, input)</code> without the
|
|
40
|
+
* server seeing the input (oprf / voprf / poprf modes; ristretto255 / P-256
|
|
41
|
+
* / P-384 / P-521 suites). The primitive behind Privacy Pass, password
|
|
42
|
+
* hardening, and private set intersection.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
var nobleCurves = require("./vendor/noble-curves.cjs");
|
|
46
|
+
var { defineClass } = require("./framework-error");
|
|
47
|
+
|
|
48
|
+
var OprfError = defineClass("OprfError", { alwaysPermanent: true });
|
|
49
|
+
|
|
50
|
+
// RFC 9497 ciphersuite name → vendored @noble/curves OPRF implementation.
|
|
51
|
+
var SUITE_IMPL = {
|
|
52
|
+
"ristretto255-sha512": nobleCurves.ristretto255_oprf,
|
|
53
|
+
"p256-sha256": nobleCurves.p256_oprf,
|
|
54
|
+
"p384-sha384": nobleCurves.p384_oprf,
|
|
55
|
+
"p521-sha512": nobleCurves.p521_oprf,
|
|
56
|
+
};
|
|
57
|
+
var SUITES = Object.keys(SUITE_IMPL);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @primitive b.crypto.oprf.suite
|
|
61
|
+
* @signature b.crypto.oprf.suite(name)
|
|
62
|
+
* @since 0.13.0
|
|
63
|
+
* @status stable
|
|
64
|
+
*
|
|
65
|
+
* Return the RFC 9497 OPRF suite for <code>name</code> — one of
|
|
66
|
+
* <code>"ristretto255-sha512"</code>, <code>"p256-sha256"</code>,
|
|
67
|
+
* <code>"p384-sha384"</code>, or <code>"p521-sha512"</code> (case
|
|
68
|
+
* insensitive). The result is <code>{ name, oprf, voprf }</code>; each mode
|
|
69
|
+
* object has the protocol functions:
|
|
70
|
+
*
|
|
71
|
+
* <ul>
|
|
72
|
+
* <li><code>deriveKeyPair(seed, info)</code> / <code>generateKeyPair()</code>
|
|
73
|
+
* → <code>{ secretKey, publicKey }</code></li>
|
|
74
|
+
* <li><code>blind(input)</code> → <code>{ blind, blinded }</code> (client)</li>
|
|
75
|
+
* <li><code>oprf.blindEvaluate(secretKey, blinded)</code> → evaluation
|
|
76
|
+
* element; <code>voprf.blindEvaluate(secretKey, publicKey, blinded)</code>
|
|
77
|
+
* → <code>{ evaluated, proof }</code> (server)</li>
|
|
78
|
+
* <li><code>oprf.finalize(input, blind, evaluation)</code> /
|
|
79
|
+
* <code>voprf.finalize(input, blind, evaluated, blinded, publicKey, proof)</code>
|
|
80
|
+
* → output bytes (client; <code>voprf</code> verifies the proof and
|
|
81
|
+
* throws if it does not match <code>publicKey</code>)</li>
|
|
82
|
+
* <li><code>evaluate(secretKey, input)</code> → output bytes (server-side,
|
|
83
|
+
* non-oblivious — equals the client's <code>finalize</code> output)</li>
|
|
84
|
+
* </ul>
|
|
85
|
+
*
|
|
86
|
+
* The partially-oblivious <code>poprf</code> mode is intentionally absent
|
|
87
|
+
* (not implemented by the vendored <code>@noble/curves</code>). Throws
|
|
88
|
+
* <code>OprfError</code> for an unknown suite name.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* var s = b.crypto.oprf.suite("ristretto255-sha512");
|
|
92
|
+
* var kp = s.oprf.deriveKeyPair(seed, Buffer.from("my-app"));
|
|
93
|
+
* var c = s.oprf.blind(Buffer.from("user@example.com")); // client
|
|
94
|
+
* var ev = s.oprf.blindEvaluate(kp.secretKey, c.blinded); // server
|
|
95
|
+
* var out = s.oprf.finalize(Buffer.from("user@example.com"), c.blind, ev);
|
|
96
|
+
* // out === s.oprf.evaluate(kp.secretKey, Buffer.from("user@example.com"))
|
|
97
|
+
*/
|
|
98
|
+
function suite(name) {
|
|
99
|
+
var impl = SUITE_IMPL[String(name).toLowerCase()];
|
|
100
|
+
if (!impl) throw new OprfError("oprf/bad-suite", "crypto.oprf.suite: unknown suite '" + name + "'; expected one of " + SUITES.join(", "));
|
|
101
|
+
// Expose only the modes the vendored @noble/curves implements (base +
|
|
102
|
+
// verifiable). poprf is omitted rather than surfaced as an empty stub.
|
|
103
|
+
return { name: impl.name, oprf: impl.oprf, voprf: impl.voprf };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
suite: suite,
|
|
108
|
+
SUITES: SUITES,
|
|
109
|
+
OprfError: OprfError,
|
|
110
|
+
};
|
|
@@ -95,6 +95,7 @@
|
|
|
95
95
|
*/
|
|
96
96
|
|
|
97
97
|
var audit = require("./audit");
|
|
98
|
+
var bCrypto = require("./crypto");
|
|
98
99
|
var { defineClass } = require("./framework-error");
|
|
99
100
|
var IabTcfError = defineClass("IabTcfError", { alwaysPermanent: true });
|
|
100
101
|
|
|
@@ -129,11 +130,15 @@ function _bitReader(buf) {
|
|
|
129
130
|
throw IabTcfError.factory("BAD_LENGTH",
|
|
130
131
|
"iabTcf: read past end of segment (offset=" + bitOffset + " want=" + n + " total=" + totalBits + ")");
|
|
131
132
|
}
|
|
133
|
+
// Accumulate with `* 2`, not `<< 1`: the Created / LastUpdated fields are
|
|
134
|
+
// 36 bits and their deciseconds values exceed 2^31 for any real date, so a
|
|
135
|
+
// 32-bit shift would silently truncate them. `* 2 + bit` stays exact up to
|
|
136
|
+
// 2^53, well above the widest TCF field.
|
|
132
137
|
var v = 0;
|
|
133
138
|
for (var i = 0; i < n; i += 1) {
|
|
134
139
|
var byteIdx = (bitOffset + i) >> 3;
|
|
135
140
|
var bitIdx = 7 - ((bitOffset + i) & 7); // allow:raw-byte-literal — high-bit-first ordering
|
|
136
|
-
v = (v
|
|
141
|
+
v = (v * 2) + ((buf[byteIdx] >> bitIdx) & 1);
|
|
137
142
|
}
|
|
138
143
|
bitOffset += n;
|
|
139
144
|
return v;
|
|
@@ -178,6 +183,7 @@ function _parseCore(buf) {
|
|
|
178
183
|
// the vendorConsents/LIs bitmaps when present.
|
|
179
184
|
var vendorConsents = _parseVendorSection(r);
|
|
180
185
|
var vendorLIs = _parseVendorSection(r);
|
|
186
|
+
var publisherRestrictions = _parsePublisherRestrictions(r);
|
|
181
187
|
return {
|
|
182
188
|
version: version,
|
|
183
189
|
createdAt: createdRaw * 100, // allow:raw-time-literal — TCF spec deciseconds → ms
|
|
@@ -197,6 +203,56 @@ function _parseCore(buf) {
|
|
|
197
203
|
publisherCC: publisherCC,
|
|
198
204
|
vendorConsents: vendorConsents,
|
|
199
205
|
vendorLIs: vendorLIs,
|
|
206
|
+
publisherRestrictions: publisherRestrictions,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Publisher restrictions follow the two core vendor sections:
|
|
211
|
+
// NumPubRestrictions (12 bits) then, per restriction, PurposeId (6) +
|
|
212
|
+
// RestrictionType (2) + a range list of vendor ids. NumPubRestrictions is
|
|
213
|
+
// mandatory even when zero, so a core that ends before it is truncated — the
|
|
214
|
+
// reader's bounds check throws rather than treating the gap as "no
|
|
215
|
+
// restrictions", which would let a malformed string validate.
|
|
216
|
+
function _parsePublisherRestrictions(r) {
|
|
217
|
+
var out = [];
|
|
218
|
+
var num = r.read(12); // allow:raw-byte-literal — TCF spec field width
|
|
219
|
+
for (var i = 0; i < num; i += 1) {
|
|
220
|
+
var purposeId = r.read(6); // allow:raw-byte-literal — TCF spec field width
|
|
221
|
+
var restrictionType = r.read(2);
|
|
222
|
+
var numEntries = r.read(12); // allow:raw-byte-literal — TCF spec field width
|
|
223
|
+
var vendorIds = [];
|
|
224
|
+
for (var e = 0; e < numEntries; e += 1) {
|
|
225
|
+
var isRange = r.read(1) === 1;
|
|
226
|
+
var startVendorId = r.read(16); // allow:raw-byte-literal — TCF spec field width
|
|
227
|
+
if (isRange) {
|
|
228
|
+
var endVendorId = r.read(16); // allow:raw-byte-literal — TCF spec field width
|
|
229
|
+
for (var v = startVendorId; v <= endVendorId; v += 1) vendorIds.push(v);
|
|
230
|
+
} else {
|
|
231
|
+
vendorIds.push(startVendorId);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
out.push({ purposeId: purposeId, restrictionType: restrictionType, vendorIds: vendorIds });
|
|
235
|
+
}
|
|
236
|
+
return out;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// PublisherTC segment (type 3): publisher purpose consent + LI bit-fields
|
|
240
|
+
// then a custom-purpose count and its two bit-fields.
|
|
241
|
+
function _parsePublisherTC(buf) {
|
|
242
|
+
var r = _bitReader(buf);
|
|
243
|
+
r.read(3); // allow:raw-byte-literal — segment-type prefix
|
|
244
|
+
var pubPurposesConsent = r.readBitField(24); // allow:raw-byte-literal — TCF spec field width
|
|
245
|
+
var pubPurposesLI = r.readBitField(24); // allow:raw-byte-literal — TCF spec field width
|
|
246
|
+
var numCustomPurposes = r.read(6); // allow:raw-byte-literal — TCF spec field width
|
|
247
|
+
var customConsent = r.readBitField(numCustomPurposes);
|
|
248
|
+
var customLI = r.readBitField(numCustomPurposes);
|
|
249
|
+
return {
|
|
250
|
+
present: true,
|
|
251
|
+
pubPurposesConsent: pubPurposesConsent,
|
|
252
|
+
pubPurposesLITransparency: pubPurposesLI,
|
|
253
|
+
numCustomPurposes: numCustomPurposes,
|
|
254
|
+
customPurposesConsent: customConsent,
|
|
255
|
+
customPurposesLITransparency: customLI,
|
|
200
256
|
};
|
|
201
257
|
}
|
|
202
258
|
|
|
@@ -298,7 +354,7 @@ function parseString(tcString) {
|
|
|
298
354
|
} else if (segType === SEGMENT_TYPE_ALLOWED_VENDORS) {
|
|
299
355
|
allowedVendors = { present: true, vendorIds: _parseSecondaryVendorSegment(segBuf, SEGMENT_TYPE_ALLOWED_VENDORS).ids };
|
|
300
356
|
} else if (segType === SEGMENT_TYPE_PUBLISHER_TC) {
|
|
301
|
-
publisherTC =
|
|
357
|
+
publisherTC = _parsePublisherTC(segBuf);
|
|
302
358
|
} else {
|
|
303
359
|
errors.push("segment[" + i + "] unknown type: " + segType);
|
|
304
360
|
}
|
|
@@ -451,8 +507,227 @@ function checkVendor(parsed, vendorId) {
|
|
|
451
507
|
};
|
|
452
508
|
}
|
|
453
509
|
|
|
510
|
+
// ---- encode ----------------------------------------------------------------
|
|
511
|
+
|
|
512
|
+
// Accept a Set, an array, or a parsed `{ ids: Set }` / `{ vendorIds: Set }`
|
|
513
|
+
// section as an id collection, and return a sorted unique array of positive
|
|
514
|
+
// integers.
|
|
515
|
+
function _idArray(x) {
|
|
516
|
+
var src = x;
|
|
517
|
+
if (x && typeof x === "object" && !Array.isArray(x) && !(x instanceof Set)) {
|
|
518
|
+
src = x.ids != null ? x.ids : x.vendorIds;
|
|
519
|
+
}
|
|
520
|
+
var list = src instanceof Set ? Array.from(src) : (Array.isArray(src) ? src : []);
|
|
521
|
+
var seen = Object.create(null);
|
|
522
|
+
var out = [];
|
|
523
|
+
list.forEach(function (id) {
|
|
524
|
+
if (typeof id !== "number" || !isFinite(id) || id < 1 || Math.floor(id) !== id) {
|
|
525
|
+
throw IabTcfError.factory("BAD_VALUE", "iabTcf.encode: vendor/purpose ids must be positive integers, got " + id);
|
|
526
|
+
}
|
|
527
|
+
if (!seen[id]) { seen[id] = 1; out.push(id); }
|
|
528
|
+
});
|
|
529
|
+
out.sort(function (a, b) { return a - b; });
|
|
530
|
+
return out;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Collapse a sorted unique id array into [start, end] runs for range encoding.
|
|
534
|
+
function _idRuns(ids) {
|
|
535
|
+
var runs = [];
|
|
536
|
+
for (var i = 0; i < ids.length; i += 1) {
|
|
537
|
+
var start = ids[i];
|
|
538
|
+
var end = start;
|
|
539
|
+
while (i + 1 < ids.length && ids[i + 1] === end + 1) { end = ids[i + 1]; i += 1; }
|
|
540
|
+
runs.push([start, end]);
|
|
541
|
+
}
|
|
542
|
+
return runs;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function _decisec(t) {
|
|
546
|
+
var ms = t instanceof Date ? t.getTime() : (t == null ? Date.now() : Number(t));
|
|
547
|
+
if (!isFinite(ms) || ms < 0) throw IabTcfError.factory("BAD_VALUE", "iabTcf.encode: timestamp must be a Date or non-negative epoch-ms");
|
|
548
|
+
return Math.round(ms / 100); // allow:raw-time-literal — ms → TCF deciseconds
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Bit writer mirroring _bitReader: bits are packed high-bit-first, then the
|
|
552
|
+
// stream is right-padded to a whole number of bytes (byte-oriented base64url,
|
|
553
|
+
// matching the reference CMP encoders and this module's own reader).
|
|
554
|
+
function _bitWriter() {
|
|
555
|
+
var bits = "";
|
|
556
|
+
function writeInt(v, n) {
|
|
557
|
+
if (typeof v !== "number" || !isFinite(v) || v < 0 || Math.floor(v) !== v) throw IabTcfError.factory("BAD_VALUE", "iabTcf.encode: expected a non-negative integer, got " + v);
|
|
558
|
+
if (v >= Math.pow(2, n)) throw IabTcfError.factory("VALUE_OVERFLOW", "iabTcf.encode: " + v + " does not fit in " + n + " bits");
|
|
559
|
+
bits += v.toString(2).padStart(n, "0");
|
|
560
|
+
}
|
|
561
|
+
function writeBool(flag) { bits += flag ? "1" : "0"; }
|
|
562
|
+
function writeBitField(ids, n) {
|
|
563
|
+
var set = Object.create(null);
|
|
564
|
+
_idArray(ids).forEach(function (id) { set[id] = 1; });
|
|
565
|
+
for (var i = 1; i <= n; i += 1) writeBool(set[i] === 1);
|
|
566
|
+
}
|
|
567
|
+
function writeVendorSection(ids) {
|
|
568
|
+
var clean = _idArray(ids);
|
|
569
|
+
var maxVendorId = clean.length ? clean[clean.length - 1] : 0;
|
|
570
|
+
writeInt(maxVendorId, 16); // allow:raw-byte-literal — TCF spec field width
|
|
571
|
+
if (maxVendorId === 0) { writeBool(false); return; }
|
|
572
|
+
var runs = _idRuns(clean);
|
|
573
|
+
var rangeBits = 1 + 12; // allow:raw-byte-literal — TCF spec field width
|
|
574
|
+
runs.forEach(function (run) { rangeBits += 1 + 16 + (run[0] === run[1] ? 0 : 16); }); // allow:raw-byte-literal — TCF spec field width
|
|
575
|
+
var bitfieldBits = 1 + maxVendorId;
|
|
576
|
+
if (rangeBits < bitfieldBits) {
|
|
577
|
+
writeBool(true);
|
|
578
|
+
writeInt(runs.length, 12); // allow:raw-byte-literal — TCF spec field width
|
|
579
|
+
runs.forEach(function (run) {
|
|
580
|
+
if (run[0] === run[1]) { writeBool(false); writeInt(run[0], 16); } // allow:raw-byte-literal — TCF spec field width
|
|
581
|
+
else { writeBool(true); writeInt(run[0], 16); writeInt(run[1], 16); } // allow:raw-byte-literal — TCF spec field width
|
|
582
|
+
});
|
|
583
|
+
} else {
|
|
584
|
+
writeBool(false);
|
|
585
|
+
writeBitField(clean, maxVendorId);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
function toBuffer() {
|
|
589
|
+
var padded = bits + "0".repeat((8 - (bits.length % 8)) % 8); // allow:raw-byte-literal — pad to whole bytes
|
|
590
|
+
var byteLen = padded.length / 8; // allow:raw-byte-literal — bits per byte
|
|
591
|
+
var out = Buffer.alloc(byteLen);
|
|
592
|
+
for (var i = 0; i < byteLen; i += 1) out[i] = parseInt(padded.slice(i * 8, i * 8 + 8), 2); // allow:raw-byte-literal — bits per byte
|
|
593
|
+
return out;
|
|
594
|
+
}
|
|
595
|
+
return { writeInt: writeInt, writeBool: writeBool, writeBitField: writeBitField, writeVendorSection: writeVendorSection, toBuffer: toBuffer };
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function _b64urlEncode(buf) {
|
|
599
|
+
return bCrypto.toBase64Url(buf);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function _writeLetters(w, s, label) {
|
|
603
|
+
var str = String(s).toUpperCase();
|
|
604
|
+
if (str.length !== 2) throw IabTcfError.factory("BAD_VALUE", "iabTcf.encode: " + label + " must be a 2-letter code, got '" + s + "'");
|
|
605
|
+
for (var i = 0; i < 2; i += 1) {
|
|
606
|
+
var v = str.charCodeAt(i) - 0x41; // allow:raw-byte-literal — ASCII 'A' offset
|
|
607
|
+
if (v < 0 || v > 25) throw IabTcfError.factory("BAD_VALUE", "iabTcf.encode: '" + str.charAt(i) + "' is not an A-Z letter");
|
|
608
|
+
w.writeInt(v, 6); // allow:raw-byte-literal — TCF spec field width
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function _encodePublisherTC(pub) {
|
|
613
|
+
var w = _bitWriter();
|
|
614
|
+
w.writeInt(SEGMENT_TYPE_PUBLISHER_TC, 3); // allow:raw-byte-literal — segment-type prefix
|
|
615
|
+
w.writeBitField(pub.pubPurposesConsent || [], 24); // allow:raw-byte-literal — TCF spec field width
|
|
616
|
+
w.writeBitField(pub.pubPurposesLITransparency || [], 24); // allow:raw-byte-literal — TCF spec field width
|
|
617
|
+
var custom = _idArray(pub.customPurposesConsent || []);
|
|
618
|
+
var customLI = _idArray(pub.customPurposesLITransparency || []);
|
|
619
|
+
var n = pub.numCustomPurposes != null
|
|
620
|
+
? pub.numCustomPurposes
|
|
621
|
+
: Math.max(custom.length ? custom[custom.length - 1] : 0, customLI.length ? customLI[customLI.length - 1] : 0);
|
|
622
|
+
w.writeInt(n, 6); // allow:raw-byte-literal — TCF spec field width
|
|
623
|
+
w.writeBitField(custom, n);
|
|
624
|
+
w.writeBitField(customLI, n);
|
|
625
|
+
return _b64urlEncode(w.toBuffer());
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* @primitive b.iabTcf.encode
|
|
630
|
+
* @signature b.iabTcf.encode(obj)
|
|
631
|
+
* @since 0.13.1
|
|
632
|
+
* @status stable
|
|
633
|
+
* @compliance iab-tcf
|
|
634
|
+
* @related b.iabTcf.parseString, b.iabTcf.isValid
|
|
635
|
+
*
|
|
636
|
+
* Serialise a TCF object — in the shape `parseString` returns — back into a
|
|
637
|
+
* TC string. Vendor and purpose collections may be `Set`s, arrays of ids, or
|
|
638
|
+
* the parsed `{ ids }` / `{ vendorIds }` sections. Vendor sections are written
|
|
639
|
+
* with whichever of the bit-field and range forms is smaller, matching the
|
|
640
|
+
* reference CMP encoders, so a parsed string round-trips to an equivalent
|
|
641
|
+
* signal. Pass `disclosedVendors` / `allowedVendors` / `publisherTC` to append
|
|
642
|
+
* those segments. Throws `IabTcfError` on a value that does not fit its field.
|
|
643
|
+
*
|
|
644
|
+
* @example
|
|
645
|
+
* var s = b.iabTcf.encode({
|
|
646
|
+
* core: { version: 2, cmpId: 5, vendorListVersion: 100, consentLanguage: "EN",
|
|
647
|
+
* purposesConsent: [1, 2, 3], vendorConsents: [1, 28, 100], publisherCC: "DE" },
|
|
648
|
+
* disclosedVendors: [1, 28, 100],
|
|
649
|
+
* });
|
|
650
|
+
*/
|
|
651
|
+
function encode(obj) {
|
|
652
|
+
if (!obj || typeof obj !== "object" || !obj.core || typeof obj.core !== "object") {
|
|
653
|
+
throw IabTcfError.factory("BAD_INPUT", "iabTcf.encode: obj must have a 'core' object");
|
|
654
|
+
}
|
|
655
|
+
var c = obj.core;
|
|
656
|
+
var w = _bitWriter();
|
|
657
|
+
w.writeInt(c.version != null ? c.version : TCF_V23_CORE_VERSION, 6); // allow:raw-byte-literal — TCF spec field width
|
|
658
|
+
w.writeInt(_decisec(c.createdAt), 36); // allow:raw-byte-literal — TCF spec field width
|
|
659
|
+
w.writeInt(_decisec(c.lastUpdatedAt != null ? c.lastUpdatedAt : c.createdAt), 36); // allow:raw-byte-literal — TCF spec field width
|
|
660
|
+
w.writeInt(c.cmpId || 0, 12); // allow:raw-byte-literal — TCF spec field width
|
|
661
|
+
w.writeInt(c.cmpVersion || 0, 12); // allow:raw-byte-literal — TCF spec field width
|
|
662
|
+
w.writeInt(c.consentScreen || 0, 6); // allow:raw-byte-literal — TCF spec field width
|
|
663
|
+
_writeLetters(w, c.consentLanguage || "EN", "consentLanguage");
|
|
664
|
+
w.writeInt(c.vendorListVersion || 0, 12); // allow:raw-byte-literal — TCF spec field width
|
|
665
|
+
w.writeInt(c.policyVersion != null ? c.policyVersion : TCF_V23_POLICY_VERSION, 6); // allow:raw-byte-literal — TCF spec field width
|
|
666
|
+
w.writeBool(c.isServiceSpecific !== false);
|
|
667
|
+
w.writeBool(c.useNonStandardStacks === true);
|
|
668
|
+
w.writeBitField(c.specialFeatureOptins || [], 12); // allow:raw-byte-literal — TCF spec field width
|
|
669
|
+
w.writeBitField(c.purposesConsent || [], 24); // allow:raw-byte-literal — TCF spec field width
|
|
670
|
+
w.writeBitField(c.purposesLI || [], 24); // allow:raw-byte-literal — TCF spec field width
|
|
671
|
+
w.writeBool(c.purposeOneTreatment === true);
|
|
672
|
+
_writeLetters(w, c.publisherCC || "AA", "publisherCC");
|
|
673
|
+
w.writeVendorSection(c.vendorConsents || []);
|
|
674
|
+
w.writeVendorSection(c.vendorLIs || []);
|
|
675
|
+
var restrictions = c.publisherRestrictions || [];
|
|
676
|
+
w.writeInt(restrictions.length, 12); // allow:raw-byte-literal — TCF spec field width
|
|
677
|
+
restrictions.forEach(function (pr) {
|
|
678
|
+
w.writeInt(pr.purposeId, 6); // allow:raw-byte-literal — TCF spec field width
|
|
679
|
+
w.writeInt(typeof pr.restrictionType === "number" ? pr.restrictionType : 0, 2);
|
|
680
|
+
var runs = _idRuns(_idArray(pr.vendorIds || []));
|
|
681
|
+
w.writeInt(runs.length, 12); // allow:raw-byte-literal — TCF spec field width
|
|
682
|
+
runs.forEach(function (run) {
|
|
683
|
+
if (run[0] === run[1]) { w.writeBool(false); w.writeInt(run[0], 16); } // allow:raw-byte-literal — TCF spec field width
|
|
684
|
+
else { w.writeBool(true); w.writeInt(run[0], 16); w.writeInt(run[1], 16); } // allow:raw-byte-literal — TCF spec field width
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
var segs = [_b64urlEncode(w.toBuffer())];
|
|
688
|
+
|
|
689
|
+
if (obj.disclosedVendors != null) {
|
|
690
|
+
var dw = _bitWriter();
|
|
691
|
+
dw.writeInt(SEGMENT_TYPE_DISCLOSED_VENDORS, 3); // allow:raw-byte-literal — segment-type prefix
|
|
692
|
+
dw.writeVendorSection(obj.disclosedVendors);
|
|
693
|
+
segs.push(_b64urlEncode(dw.toBuffer()));
|
|
694
|
+
}
|
|
695
|
+
if (obj.allowedVendors != null) {
|
|
696
|
+
var aw = _bitWriter();
|
|
697
|
+
aw.writeInt(SEGMENT_TYPE_ALLOWED_VENDORS, 3); // allow:raw-byte-literal — segment-type prefix
|
|
698
|
+
aw.writeVendorSection(obj.allowedVendors);
|
|
699
|
+
segs.push(_b64urlEncode(aw.toBuffer()));
|
|
700
|
+
}
|
|
701
|
+
if (obj.publisherTC != null && obj.publisherTC.present !== false) {
|
|
702
|
+
segs.push(_encodePublisherTC(obj.publisherTC));
|
|
703
|
+
}
|
|
704
|
+
return segs.join(".");
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* @primitive b.iabTcf.isValid
|
|
709
|
+
* @signature b.iabTcf.isValid(tcString)
|
|
710
|
+
* @since 0.13.1
|
|
711
|
+
* @status stable
|
|
712
|
+
* @compliance iab-tcf
|
|
713
|
+
* @related b.iabTcf.parseString
|
|
714
|
+
*
|
|
715
|
+
* Return `true` if the string parses as a well-formed TCF Core segment,
|
|
716
|
+
* `false` otherwise. A total predicate — never throws. Note this checks
|
|
717
|
+
* structural validity only; use `requireV23Disclosed` for the v2.3 policy gate.
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* b.iabTcf.isValid("CQSbk4AQSbk4ANwAAAENAwCgAAAAAAAAAAYgACPAAAAA"); // → true
|
|
721
|
+
* b.iabTcf.isValid("nonsense"); // → false
|
|
722
|
+
*/
|
|
723
|
+
function isValid(tcString) {
|
|
724
|
+
try { parseString(tcString); return true; } catch (_e) { return false; }
|
|
725
|
+
}
|
|
726
|
+
|
|
454
727
|
module.exports = {
|
|
455
728
|
parseString: parseString,
|
|
729
|
+
encode: encode,
|
|
730
|
+
isValid: isValid,
|
|
456
731
|
requireV23Disclosed: requireV23Disclosed,
|
|
457
732
|
checkVendor: checkVendor,
|
|
458
733
|
IabTcfError: IabTcfError,
|