@blamejs/core 0.8.42 → 0.8.49
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 +93 -0
- package/README.md +10 -10
- package/index.js +52 -0
- package/lib/a2a.js +159 -34
- package/lib/acme.js +762 -0
- package/lib/ai-pref.js +166 -43
- package/lib/api-key.js +108 -47
- package/lib/api-snapshot.js +157 -40
- package/lib/app-shutdown.js +113 -77
- package/lib/archive.js +337 -40
- package/lib/arg-parser.js +697 -0
- package/lib/asyncapi.js +99 -55
- package/lib/atomic-file.js +465 -104
- package/lib/audit-chain.js +123 -34
- package/lib/audit-daily-review.js +389 -0
- package/lib/audit-sign.js +302 -56
- package/lib/audit-tools.js +412 -63
- package/lib/audit.js +656 -35
- package/lib/auth/jwt-external.js +17 -0
- package/lib/auth/oauth.js +7 -0
- package/lib/auth-bot-challenge.js +505 -0
- package/lib/auth-header.js +92 -25
- package/lib/backup/bundle.js +26 -0
- package/lib/backup/index.js +512 -89
- package/lib/backup/manifest.js +168 -7
- package/lib/break-glass.js +415 -39
- package/lib/budr.js +103 -30
- package/lib/bundler.js +86 -66
- package/lib/cache.js +192 -72
- package/lib/chain-writer.js +65 -40
- package/lib/circuit-breaker.js +56 -33
- package/lib/cli-helpers.js +106 -75
- package/lib/cli.js +6 -30
- package/lib/cloud-events.js +99 -32
- package/lib/cluster-storage.js +162 -37
- package/lib/cluster.js +340 -49
- package/lib/codepoint-class.js +66 -0
- package/lib/compliance.js +424 -24
- package/lib/config-drift.js +111 -46
- package/lib/config.js +94 -40
- package/lib/consent.js +165 -18
- package/lib/constants.js +1 -0
- package/lib/content-credentials.js +153 -48
- package/lib/cookies.js +154 -62
- package/lib/credential-hash.js +133 -61
- package/lib/crypto-field.js +702 -18
- package/lib/crypto-hpke.js +256 -0
- package/lib/crypto.js +744 -22
- package/lib/csv.js +178 -35
- package/lib/daemon.js +456 -0
- package/lib/dark-patterns.js +186 -55
- package/lib/db-query.js +79 -2
- package/lib/db.js +1431 -60
- package/lib/ddl-change-control.js +523 -0
- package/lib/deprecate.js +195 -40
- package/lib/dev.js +82 -39
- package/lib/dora.js +67 -48
- package/lib/dr-runbook.js +368 -0
- package/lib/dsr.js +142 -11
- package/lib/dual-control.js +91 -56
- package/lib/events.js +120 -41
- package/lib/external-db-migrate.js +192 -2
- package/lib/external-db.js +795 -50
- package/lib/fapi2.js +122 -1
- package/lib/fda-21cfr11.js +395 -0
- package/lib/fdx.js +132 -2
- package/lib/file-type.js +87 -0
- package/lib/file-upload.js +93 -0
- package/lib/flag.js +82 -20
- package/lib/forms.js +132 -29
- package/lib/framework-error.js +169 -0
- package/lib/framework-schema.js +163 -35
- package/lib/gate-contract.js +849 -175
- package/lib/graphql-federation.js +68 -7
- package/lib/guard-all.js +172 -55
- package/lib/guard-archive.js +286 -124
- package/lib/guard-auth.js +194 -21
- package/lib/guard-cidr.js +190 -28
- package/lib/guard-csv.js +397 -51
- package/lib/guard-domain.js +213 -91
- package/lib/guard-email.js +236 -29
- package/lib/guard-filename.js +307 -75
- package/lib/guard-graphql.js +263 -30
- package/lib/guard-html.js +310 -116
- package/lib/guard-image.js +243 -30
- package/lib/guard-json.js +260 -54
- package/lib/guard-jsonpath.js +235 -23
- package/lib/guard-jwt.js +284 -30
- package/lib/guard-markdown.js +204 -22
- package/lib/guard-mime.js +190 -26
- package/lib/guard-oauth.js +277 -28
- package/lib/guard-pdf.js +251 -27
- package/lib/guard-regex.js +226 -18
- package/lib/guard-shell.js +229 -26
- package/lib/guard-svg.js +177 -10
- package/lib/guard-template.js +232 -21
- package/lib/guard-time.js +195 -29
- package/lib/guard-uuid.js +189 -30
- package/lib/guard-xml.js +259 -36
- package/lib/guard-yaml.js +241 -44
- package/lib/honeytoken.js +63 -27
- package/lib/html-balance.js +83 -0
- package/lib/http-client.js +486 -59
- package/lib/http-message-signature.js +582 -0
- package/lib/i18n.js +102 -49
- package/lib/iab-mspa.js +112 -32
- package/lib/iab-tcf.js +107 -2
- package/lib/inbox.js +90 -52
- package/lib/keychain.js +865 -0
- package/lib/legal-hold.js +374 -0
- package/lib/local-db-thin.js +320 -0
- package/lib/log-stream.js +281 -51
- package/lib/log.js +184 -86
- package/lib/mail-bounce.js +107 -62
- package/lib/mail.js +295 -58
- package/lib/mcp.js +108 -27
- package/lib/metrics.js +98 -89
- package/lib/middleware/age-gate.js +36 -0
- package/lib/middleware/ai-act-disclosure.js +37 -0
- package/lib/middleware/api-encrypt.js +45 -0
- package/lib/middleware/assetlinks.js +40 -0
- package/lib/middleware/asyncapi-serve.js +35 -0
- package/lib/middleware/attach-user.js +40 -0
- package/lib/middleware/bearer-auth.js +40 -0
- package/lib/middleware/body-parser.js +230 -0
- package/lib/middleware/bot-disclose.js +34 -0
- package/lib/middleware/bot-guard.js +39 -0
- package/lib/middleware/compression.js +37 -0
- package/lib/middleware/cookies.js +32 -0
- package/lib/middleware/cors.js +40 -0
- package/lib/middleware/csp-nonce.js +40 -0
- package/lib/middleware/csp-report.js +34 -0
- package/lib/middleware/csrf-protect.js +43 -0
- package/lib/middleware/daily-byte-quota.js +53 -85
- package/lib/middleware/db-role-for.js +40 -0
- package/lib/middleware/dpop.js +40 -0
- package/lib/middleware/error-handler.js +37 -14
- package/lib/middleware/fetch-metadata.js +39 -0
- package/lib/middleware/flag-context.js +34 -0
- package/lib/middleware/gpc.js +33 -0
- package/lib/middleware/headers.js +35 -0
- package/lib/middleware/health.js +46 -0
- package/lib/middleware/host-allowlist.js +30 -0
- package/lib/middleware/network-allowlist.js +38 -0
- package/lib/middleware/openapi-serve.js +34 -0
- package/lib/middleware/rate-limit.js +160 -18
- package/lib/middleware/request-id.js +36 -18
- package/lib/middleware/request-log.js +37 -0
- package/lib/middleware/require-aal.js +29 -0
- package/lib/middleware/require-auth.js +32 -0
- package/lib/middleware/require-bound-key.js +41 -0
- package/lib/middleware/require-content-type.js +32 -0
- package/lib/middleware/require-methods.js +27 -0
- package/lib/middleware/require-mtls.js +33 -0
- package/lib/middleware/require-step-up.js +37 -0
- package/lib/middleware/security-headers.js +44 -0
- package/lib/middleware/security-txt.js +38 -0
- package/lib/middleware/span-http-server.js +37 -0
- package/lib/middleware/sse.js +36 -0
- package/lib/middleware/trace-log-correlation.js +33 -0
- package/lib/middleware/trace-propagate.js +32 -0
- package/lib/middleware/tus-upload.js +90 -0
- package/lib/middleware/web-app-manifest.js +53 -0
- package/lib/mtls-ca.js +100 -70
- package/lib/network-byte-quota.js +308 -0
- package/lib/network-heartbeat.js +135 -0
- package/lib/network-tls.js +534 -4
- package/lib/network.js +103 -0
- package/lib/notify.js +114 -43
- package/lib/ntp-check.js +192 -51
- package/lib/observability.js +145 -47
- package/lib/openapi.js +90 -44
- package/lib/outbox.js +99 -1
- package/lib/pagination.js +168 -86
- package/lib/parsers/index.js +16 -5
- package/lib/permissions.js +93 -40
- package/lib/pqc-agent.js +84 -8
- package/lib/pqc-software.js +94 -60
- package/lib/process-spawn.js +95 -21
- package/lib/pubsub.js +96 -66
- package/lib/queue.js +375 -54
- package/lib/redact.js +793 -21
- package/lib/render.js +139 -47
- package/lib/request-helpers.js +485 -121
- package/lib/restore-bundle.js +142 -39
- package/lib/restore-rollback.js +136 -45
- package/lib/retention.js +178 -50
- package/lib/retry.js +116 -33
- package/lib/router.js +475 -23
- package/lib/safe-async.js +543 -94
- package/lib/safe-buffer.js +337 -41
- package/lib/safe-json.js +467 -62
- package/lib/safe-jsonpath.js +285 -0
- package/lib/safe-schema.js +631 -87
- package/lib/safe-sql.js +221 -59
- package/lib/safe-url.js +278 -46
- package/lib/sandbox-worker.js +135 -0
- package/lib/sandbox.js +358 -0
- package/lib/scheduler.js +135 -70
- package/lib/self-update.js +647 -0
- package/lib/session-device-binding.js +431 -0
- package/lib/session.js +259 -49
- package/lib/slug.js +138 -26
- package/lib/ssrf-guard.js +316 -56
- package/lib/storage.js +433 -70
- package/lib/subject.js +405 -23
- package/lib/template.js +148 -8
- package/lib/tenant-quota.js +545 -0
- package/lib/testing.js +440 -53
- package/lib/time.js +291 -23
- package/lib/tls-exporter.js +239 -0
- package/lib/tracing.js +90 -74
- package/lib/uuid.js +97 -22
- package/lib/vault/index.js +284 -22
- package/lib/vault/seal-pem-file.js +66 -0
- package/lib/watcher.js +368 -0
- package/lib/webhook.js +196 -63
- package/lib/websocket.js +393 -68
- package/lib/wiki-concepts.js +338 -0
- package/lib/worker-pool.js +464 -0
- package/package.json +3 -3
- package/sbom.cyclonedx.json +7 -7
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* b.crypto.hpke — RFC 9180 Hybrid Public-Key Encryption (HPKE).
|
|
4
|
+
*
|
|
5
|
+
* Suite (PQC-first per framework crypto policy):
|
|
6
|
+
* KEM: ML-KEM-1024 (FIPS 203) — post-quantum encapsulation
|
|
7
|
+
* KDF: HKDF-SHA3-512
|
|
8
|
+
* AEAD: ChaCha20-Poly1305 (RFC 7539)
|
|
9
|
+
*
|
|
10
|
+
* The classical HPKE suites in RFC 9180 §7 (DHKEM with X25519 / P-256 /
|
|
11
|
+
* P-384 / P-521 + HKDF-SHA256/384/512 + AES-GCM/ChaCha20) are NOT
|
|
12
|
+
* exposed — the framework's PQC-first defaults rule out classical-only
|
|
13
|
+
* key agreement and AES-GCM. Operators wanting the IANA-registered
|
|
14
|
+
* suite codepoints for cross-system interop use the dedicated
|
|
15
|
+
* b.crypto.encryptMlkem768X25519 helper which speaks the IETF / Chrome
|
|
16
|
+
* TLS-1.3 hybrid format directly.
|
|
17
|
+
*
|
|
18
|
+
* Per RFC 9180 §5.1 the KDF MUST absorb a suite_id binding along with
|
|
19
|
+
* the shared secret so a key derived under one suite cannot be silently
|
|
20
|
+
* reused under another. The same FixedInfo construction lib/crypto.js
|
|
21
|
+
* uses (NIST SP 800-56C r2 §4.1 OtherInfo) carries the kem-id /
|
|
22
|
+
* cipher-id / kdf-id triple plus the framework label.
|
|
23
|
+
*
|
|
24
|
+
* Operator API (single-shot per RFC 9180 §6.1, mode_base):
|
|
25
|
+
*
|
|
26
|
+
* var pair = b.crypto.hpke.generateKeyPair();
|
|
27
|
+
* // → { publicKey, privateKey } (ML-KEM-1024 PEM)
|
|
28
|
+
*
|
|
29
|
+
* var sealed = b.crypto.hpke.seal({
|
|
30
|
+
* recipientPubKey: pair.publicKey, // ML-KEM-1024 PEM
|
|
31
|
+
* plaintext: Buffer | string,
|
|
32
|
+
* info: Buffer | string, // application-supplied label
|
|
33
|
+
* aad: Buffer | string, // additional authenticated data
|
|
34
|
+
* });
|
|
35
|
+
* // → { enc: Buffer, ciphertext: Buffer }
|
|
36
|
+
*
|
|
37
|
+
* var pt = b.crypto.hpke.open({
|
|
38
|
+
* privateKey: pair.privateKey,
|
|
39
|
+
* enc: sealed.enc,
|
|
40
|
+
* ciphertext: sealed.ciphertext,
|
|
41
|
+
* info: "...",
|
|
42
|
+
* aad: "...",
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* `enc` is the KEM ciphertext (output of Encap); `ciphertext` is the
|
|
46
|
+
* AEAD output. `info` is the RFC 9180 §5.1 application context string;
|
|
47
|
+
* `aad` is the per-message AEAD AAD. Both bind the derived key into
|
|
48
|
+
* the application's domain so cross-context substitution is detected.
|
|
49
|
+
*
|
|
50
|
+
* Validation policy: throw at config/call site for bad input
|
|
51
|
+
* (recipientPubKey shape, plaintext type, missing private key on open).
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
var nodeCrypto = require("crypto");
|
|
55
|
+
var C = require("./constants");
|
|
56
|
+
var lazyRequire = require("./lazy-require");
|
|
57
|
+
var validateOpts = require("./validate-opts");
|
|
58
|
+
var { xchacha20poly1305 } = require("./vendor/noble-ciphers.cjs");
|
|
59
|
+
var { HpkeError } = require("./framework-error");
|
|
60
|
+
|
|
61
|
+
var _err = HpkeError.factory;
|
|
62
|
+
|
|
63
|
+
var observability = lazyRequire(function () { return require("./observability"); });
|
|
64
|
+
var audit = lazyRequire(function () { return require("./audit"); });
|
|
65
|
+
|
|
66
|
+
// HPKE suite identifiers — values picked to NOT collide with the
|
|
67
|
+
// envelope KEM_IDS namespace (those are < 0x10) so a future audit that
|
|
68
|
+
// greps for byte 0x21 finds the HPKE seal output cleanly. The FixedInfo
|
|
69
|
+
// label distinguishes HPKE-derived keys from the framework's bulk
|
|
70
|
+
// envelope KDF inputs.
|
|
71
|
+
var HPKE_SUITE_LABEL = "blamejs/hpke/v1";
|
|
72
|
+
var HPKE_KEM_ID = 0x21; // ML-KEM-1024
|
|
73
|
+
var HPKE_KDF_ID = 0x22; // HKDF-SHA3-512
|
|
74
|
+
var HPKE_AEAD_ID = 0x23; // ChaCha20-Poly1305
|
|
75
|
+
|
|
76
|
+
var HPKE_KEY_LEN = C.BYTES.bytes(32);
|
|
77
|
+
var HPKE_NONCE_LEN = C.BYTES.bytes(24);
|
|
78
|
+
|
|
79
|
+
// _suiteFixedInfo — RFC 9180 §5.1 suite_id binding. Mirrors the shape
|
|
80
|
+
// in lib/crypto.js but with the HPKE label so HPKE-derived keys cannot
|
|
81
|
+
// collide with envelope-derived keys.
|
|
82
|
+
function _suiteFixedInfo(info) {
|
|
83
|
+
var infoBuf = info == null ? Buffer.alloc(0)
|
|
84
|
+
: Buffer.isBuffer(info) ? info : Buffer.from(String(info), "utf8");
|
|
85
|
+
return Buffer.concat([
|
|
86
|
+
Buffer.from(HPKE_SUITE_LABEL, "utf8"),
|
|
87
|
+
Buffer.from([0x00, HPKE_KEM_ID, HPKE_KDF_ID, HPKE_AEAD_ID, 0x00]),
|
|
88
|
+
infoBuf,
|
|
89
|
+
]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// HKDF-SHA3-512 — RFC 5869, swapping the underlying hash to SHA3-512.
|
|
93
|
+
// Node's crypto.hkdfSync supports sha3-512 directly.
|
|
94
|
+
function _hkdfSha3(ikm, salt, info, length) {
|
|
95
|
+
return Buffer.from(nodeCrypto.hkdfSync("sha3-512", ikm, salt || Buffer.alloc(0), info, length));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// generateKeyPair — operator helper for an ML-KEM-1024 keypair sized
|
|
99
|
+
// for the HPKE suite. Distinct from b.crypto.generateEncryptionKeyPair
|
|
100
|
+
// which returns a hybrid {mlkem, ecdh-p384} keypair.
|
|
101
|
+
function generateKeyPair() {
|
|
102
|
+
var pair = nodeCrypto.generateKeyPairSync("ml-kem-1024", {
|
|
103
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
104
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" },
|
|
105
|
+
});
|
|
106
|
+
return { publicKey: pair.publicKey, privateKey: pair.privateKey };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function _validateSealOpts(opts) {
|
|
110
|
+
validateOpts.requireObject(opts, "hpke.seal", HpkeError);
|
|
111
|
+
validateOpts.requireNonEmptyString(opts.recipientPubKey,
|
|
112
|
+
"hpke.seal: recipientPubKey", HpkeError, "BAD_OPT");
|
|
113
|
+
if (opts.plaintext == null) {
|
|
114
|
+
throw _err("BAD_OPT", "hpke.seal: plaintext required");
|
|
115
|
+
}
|
|
116
|
+
if (typeof opts.plaintext !== "string" && !Buffer.isBuffer(opts.plaintext)) {
|
|
117
|
+
throw _err("BAD_OPT", "hpke.seal: plaintext must be a string or Buffer, got " + typeof opts.plaintext);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function _validateOpenOpts(opts) {
|
|
122
|
+
validateOpts.requireObject(opts, "hpke.open", HpkeError);
|
|
123
|
+
validateOpts.requireNonEmptyString(opts.privateKey,
|
|
124
|
+
"hpke.open: privateKey", HpkeError, "BAD_OPT");
|
|
125
|
+
if (!Buffer.isBuffer(opts.enc)) {
|
|
126
|
+
throw _err("BAD_OPT", "hpke.open: enc must be a Buffer (KEM ciphertext)");
|
|
127
|
+
}
|
|
128
|
+
if (!Buffer.isBuffer(opts.ciphertext)) {
|
|
129
|
+
throw _err("BAD_OPT", "hpke.open: ciphertext must be a Buffer (AEAD output)");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function _toBuf(v) {
|
|
134
|
+
if (v == null) return Buffer.alloc(0);
|
|
135
|
+
return Buffer.isBuffer(v) ? v : Buffer.from(String(v), "utf8");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// seal — RFC 9180 §6.1 SealBase. Returns { enc, ciphertext }; nonce is
|
|
139
|
+
// fixed-zero per single-shot mode (each Encap produces a fresh shared
|
|
140
|
+
// secret, so nonce-reuse is structurally impossible across messages).
|
|
141
|
+
function seal(opts) {
|
|
142
|
+
_validateSealOpts(opts);
|
|
143
|
+
var info = _toBuf(opts.info);
|
|
144
|
+
var aad = _toBuf(opts.aad);
|
|
145
|
+
|
|
146
|
+
var recipientPub = nodeCrypto.createPublicKey(opts.recipientPubKey);
|
|
147
|
+
var encap;
|
|
148
|
+
try {
|
|
149
|
+
encap = nodeCrypto.encapsulate(recipientPub);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
throw _err("KEM_ENCAP_FAILED", "hpke.seal: KEM encapsulate failed: " + e.message);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Per RFC 9180 §5.1: KEY = LabeledExtractAndExpand(shared_secret,
|
|
155
|
+
// suite_id || info, length=Nk). HKDF-SHA3-512 with the suite-bound
|
|
156
|
+
// info string subsumes the LabeledExtract / LabeledExpand pair.
|
|
157
|
+
var key = _hkdfSha3(encap.sharedKey, Buffer.alloc(0),
|
|
158
|
+
_suiteFixedInfo(info), HPKE_KEY_LEN);
|
|
159
|
+
// Single-shot zero nonce — fresh shared secret per call removes the
|
|
160
|
+
// sequence-number / nonce-reuse risk that the streaming HPKE modes
|
|
161
|
+
// require Nn == 12-byte counter for. ChaCha20-Poly1305 needs a
|
|
162
|
+
// 12-byte nonce, but the framework's AEAD wrapper uses XChaCha20-
|
|
163
|
+
// Poly1305 (24-byte nonce) which is identical security-wise — we
|
|
164
|
+
// pass a 24-byte zero nonce so the suite_id absorbs everything that
|
|
165
|
+
// could collide.
|
|
166
|
+
var nonce = Buffer.alloc(HPKE_NONCE_LEN);
|
|
167
|
+
var ct;
|
|
168
|
+
try {
|
|
169
|
+
ct = xchacha20poly1305(key, nonce, aad).encrypt(_toBuf(opts.plaintext));
|
|
170
|
+
} catch (e) {
|
|
171
|
+
throw _err("AEAD_ENCRYPT_FAILED", "hpke.seal: AEAD encrypt failed: " + e.message);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try { observability().safeEvent("hpke.seal", 1, { outcome: "success" }); }
|
|
175
|
+
catch (_e) { /* drop-silent — observability emits best-effort */ }
|
|
176
|
+
|
|
177
|
+
// Audit-everything posture: every seal/open is a primitive-level
|
|
178
|
+
// event so operators with PHI / PCI columns under HPKE can prove
|
|
179
|
+
// every encrypt-side touch. The audit is best-effort; a failing
|
|
180
|
+
// bus does not fail the seal.
|
|
181
|
+
try {
|
|
182
|
+
audit().safeEmit({
|
|
183
|
+
action: "system.hpke.seal",
|
|
184
|
+
outcome: "success",
|
|
185
|
+
metadata: { encBytes: encap.ciphertext.length, ctBytes: ct.length },
|
|
186
|
+
});
|
|
187
|
+
} catch (_e) { /* drop-silent */ }
|
|
188
|
+
|
|
189
|
+
return { enc: Buffer.from(encap.ciphertext), ciphertext: Buffer.from(ct) };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// open — RFC 9180 §6.1 OpenBase. Inverse of seal; throws HpkeError on
|
|
193
|
+
// AEAD tag verification failure.
|
|
194
|
+
function open(opts) {
|
|
195
|
+
_validateOpenOpts(opts);
|
|
196
|
+
var info = _toBuf(opts.info);
|
|
197
|
+
var aad = _toBuf(opts.aad);
|
|
198
|
+
|
|
199
|
+
var priv = nodeCrypto.createPrivateKey(opts.privateKey);
|
|
200
|
+
var sharedSecret;
|
|
201
|
+
try {
|
|
202
|
+
sharedSecret = nodeCrypto.decapsulate(priv, opts.enc);
|
|
203
|
+
} catch (e) {
|
|
204
|
+
throw _err("KEM_DECAP_FAILED", "hpke.open: KEM decapsulate failed: " + e.message);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
var key = _hkdfSha3(sharedSecret, Buffer.alloc(0),
|
|
208
|
+
_suiteFixedInfo(info), HPKE_KEY_LEN);
|
|
209
|
+
var nonce = Buffer.alloc(HPKE_NONCE_LEN);
|
|
210
|
+
var pt;
|
|
211
|
+
try {
|
|
212
|
+
pt = xchacha20poly1305(key, nonce, aad).decrypt(opts.ciphertext);
|
|
213
|
+
} catch (_e) {
|
|
214
|
+
try { observability().safeEvent("hpke.open", 1, { outcome: "failure", reason: "aead-tag" }); }
|
|
215
|
+
catch (_e) { /* drop-silent */ }
|
|
216
|
+
try {
|
|
217
|
+
audit().safeEmit({
|
|
218
|
+
action: "system.hpke.open",
|
|
219
|
+
outcome: "failure",
|
|
220
|
+
reason: "aead-tag",
|
|
221
|
+
metadata: { ctBytes: opts.ciphertext.length },
|
|
222
|
+
});
|
|
223
|
+
} catch (_e) { /* drop-silent */ }
|
|
224
|
+
throw _err("AEAD_DECRYPT_FAILED", "hpke.open: AEAD tag verification failed");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try { observability().safeEvent("hpke.open", 1, { outcome: "success" }); }
|
|
228
|
+
catch (_e) { /* drop-silent */ }
|
|
229
|
+
try {
|
|
230
|
+
audit().safeEmit({
|
|
231
|
+
action: "system.hpke.open",
|
|
232
|
+
outcome: "success",
|
|
233
|
+
metadata: { ctBytes: opts.ciphertext.length },
|
|
234
|
+
});
|
|
235
|
+
} catch (_e) { /* drop-silent */ }
|
|
236
|
+
|
|
237
|
+
return Buffer.from(pt);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// SUPPORTED_SUITE — operator-discoverable description of the active
|
|
241
|
+
// HPKE suite. The framework ships exactly one suite (PQC-first); future
|
|
242
|
+
// additions land here as a frozen-list entry alongside the existing one.
|
|
243
|
+
var SUPPORTED_SUITE = Object.freeze({
|
|
244
|
+
kem: "ML-KEM-1024",
|
|
245
|
+
kdf: "HKDF-SHA3-512",
|
|
246
|
+
aead: "ChaCha20-Poly1305",
|
|
247
|
+
label: HPKE_SUITE_LABEL,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
module.exports = {
|
|
251
|
+
generateKeyPair: generateKeyPair,
|
|
252
|
+
seal: seal,
|
|
253
|
+
open: open,
|
|
254
|
+
SUPPORTED_SUITE: SUPPORTED_SUITE,
|
|
255
|
+
HpkeError: HpkeError,
|
|
256
|
+
};
|