@blamejs/pki 0.8.60 → 0.9.0
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 +48 -0
- package/README.md +7 -0
- package/index.js +55 -26
- package/lib/acme.js +5 -5
- package/lib/asn1-der.js +1 -1
- package/lib/attrcert-sign.js +2 -2
- package/lib/cbor-det.js +4 -1
- package/lib/cmc-build.js +15 -10
- package/lib/cmp-build.js +5 -5
- package/lib/cmp-session.js +6 -3
- package/lib/cmp-verify.js +1 -1
- package/lib/cms-compress.js +5 -2
- package/lib/cms-decrypt.js +17 -17
- package/lib/cms-digest.js +2 -3
- package/lib/cms-encrypt.js +17 -18
- package/lib/cms-sign.js +2 -2
- package/lib/composite-kem.js +28 -29
- package/lib/crl-sign.js +3 -3
- package/lib/crmf-sign.js +12 -18
- package/lib/csr-sign.js +1 -1
- package/lib/ct.js +25 -22
- package/lib/est.js +12 -12
- package/lib/framework-error.js +1 -1
- package/lib/guard-bytes.js +1 -1
- package/lib/guard-crypto.js +506 -14
- package/lib/guard-limits.js +3 -2
- package/lib/guard-secret.js +3 -2
- package/lib/hpke.js +26 -26
- package/lib/http-digest.js +5 -7
- package/lib/http-retry-after.js +4 -1
- package/lib/http-transport.js +6 -6
- package/lib/inspect.js +9 -4
- package/lib/jose.js +3 -3
- package/lib/key.js +41 -29
- package/lib/merkle.js +4 -4
- package/lib/ocsp.js +3 -4
- package/lib/path-validate.js +3 -3
- package/lib/pbes2.js +14 -11
- package/lib/pkcs12-build.js +10 -11
- package/lib/pki-build.js +3 -3
- package/lib/scep.js +8 -9
- package/lib/schema-engine.js +12 -12
- package/lib/schema-pkix.js +12 -12
- package/lib/schema-tsp.js +3 -3
- package/lib/shbs.js +3 -6
- package/lib/sign-scheme.js +4 -4
- package/lib/sigstore.js +10 -10
- package/lib/smime.js +12 -9
- package/lib/tlog.js +5 -4
- package/lib/tls-cert-compress.js +3 -3
- package/lib/tsp-sign.js +6 -6
- package/lib/tuf.js +6 -5
- package/lib/validator-cose.js +2 -2
- package/lib/webauthn-mds.js +4 -8
- package/lib/webauthn.js +5 -12
- package/lib/webcrypto.js +61 -46
- package/lib/x509-sign.js +3 -3
- package/package.json +1 -1
- package/sbom.cdx.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,54 @@ All notable changes to `@blamejs/pki` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this
|
|
5
5
|
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## v0.9.0 — 2026-10-08
|
|
8
|
+
|
|
9
|
+
Import any namespace by name from an ESM caller, and give every record the toolkit builds for an omitted argument no prototype.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Every namespace is an ESM named import. `import { x509, cms, path, schema, lint, webcrypto } from "@blamejs/pki"` resolves, as does any of the other 41; `import pki from "@blamejs/pki"` is unchanged and gives the same object `require` does. The package remains CommonJS, runs on Node LTS as shipped, and adds no build step and no transpilation: every value in the export object is a bare identifier bound above it, which is the shape Node's export reader can follow. A `.d.ts` generated from the source comment blocks is still to come.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- A record the toolkit defaults for an omitted argument no longer inherits from `Object.prototype`. A caller passing its own object is unaffected: only the record built when the argument is absent changed, and a field read off it that the caller did not supply now reads as absent.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- A revocation deadline is measured against the toolkit's own clock. `pki.path.fetchingChecker` builds its deadline with no options record, so the record it defaulted carried `Object.prototype` and the clock came from an inherited `now`: one reporting a time far ahead made the deadline expired before the first fetch, and the fetch that would have retrieved a CRL or an OCSP response was never made. The same record supplies the bound a decoded size is held to, where an inherited `max` of zero refuses every value.
|
|
22
|
+
- `pki.inspect.asn1`, `pki.inspect.certificate` and the other report verbs are held to the caps their caller passed. With the options argument omitted the record they defaulted carried `Object.prototype`, so an inherited `maxBytes` became the cap the render was held to and a report that renders normally came back refused.
|
|
23
|
+
- A signer signs with the identity its caller named. `pki.x509.sign`, `pki.crl.sign` and `pki.attrcert.sign` default an omitted issuer or extension record, so an inherited `cert`, `key`, `name`, `publicKey`, `basicConstraints` or `keyUsage` was read as if supplied. Reaching the read required getting past the spec's unknown-field door and its copy depth cap, both of which refused it, so the effect was a refused signing call rather than a signature under another identity. The record has no prototype now, which is where the answer belongs.
|
|
24
|
+
- A client believes what a server actually sent. `pki.acme`, `pki.cmp`, `pki.ct`, `pki.est`, `pki.scep` and the revocation fetcher each default an omitted response record, where an inherited `status`, `body` or `headers` stands in for what arrived. The content type a CMP transfer and a CT fetch check, and the declared content length the HTTP transport enforces its response cap against, were each read through a header record defaulted the same way.
|
|
25
|
+
- The records every other verb defaults are the same shape and are converted with them: the options records of `pki.asn1.decode`, `pki.cbor.decode`, the schema engine and the shared PKIX sub-schemas, the signer and countersigner records in `pki.cms.sign`, the MAC and bag encryption records in `pki.pkcs12.build`, the message-imprint records in `pki.tsp.sign`, the header records in `pki.est`, the request and defaults records in the HTTP transport, the setup records in `pki.hpke`, the client option records in `pki.ct`, the certificate-transparency policy `pki.path.validate` passes on, the fallback key in `pki.jose`, the algorithm records the WebAuthn verifiers read a name off, and the algorithm record a signature scheme reads off a key.
|
|
26
|
+
|
|
27
|
+
### Documentation
|
|
28
|
+
|
|
29
|
+
- `THREAT-MODEL.md` states the boundary the toolkit's use of captured operations rests on. The operations a check decides through are copied from the runtime while the modules load, which is what makes a later replacement unable to change a verdict, and that also means code running before the first `require` of the package can replace an operation before the copy is taken. Load the package before running untrusted code in the same realm.
|
|
30
|
+
|
|
31
|
+
## v0.8.61 — 2026-10-07
|
|
32
|
+
|
|
33
|
+
Take every cryptographic operation, and every question about which key a key is, through an operation captured when the toolkit loads, so a replacement installed afterwards cannot decide a secret, a tag, a key type or a verdict.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- The internal crypto guard gained the operations the library needed to stop reading `node:crypto` at call time: randomness, the password-based and extract-and-expand derivations, the RSA transforms, the elliptic-curve point derivation and conversion, the AEAD transform methods, the hash handle for a caller that cannot hold its whole input, and the four key-identity accessors. The digest primitive takes input and output encodings. None of this is on the public surface.
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- `pki.webcrypto.subtle.exportKey` and `pki.key.export` read a key's type at most twice per call, once to admit the key and once for every decision that follows. Nothing in the public surface changed shape, and no error code was added or removed.
|
|
42
|
+
|
|
43
|
+
### Fixed
|
|
44
|
+
|
|
45
|
+
- An imported key is bound to the algorithm the caller named it under. `pki.webcrypto.subtle.importKey` settles that by asking the key which algorithm family it belongs to, and that answer came from an accessor on a node prototype, read at the moment of the check. An accessor answering `rsa` for an Ed25519 key therefore made the import accept it as RSA-PSS, and every later operation ran under an algorithm the key does not belong to. The accessor is captured when the engine loads. The same read also decided a curve, a modulus length and whether a key pair corresponds, across `pki.key`, `pki.tuf`, `pki.tlog`, `pki.sigstore`, `pki.cmc`, `pki.crmf`, `pki.ct` and the signature-scheme check that matches a signer to a certificate.
|
|
46
|
+
- A plain object carrying an `asymmetricKeyType` field is no longer accepted as a key. The sites that asked whether a caller had handed them a key object tested for that one field, which any object can carry, so a forged object passed the test and was then used as a key. The question is asked of the object's prototype now, and the answer settles both whether it is a key and which kind.
|
|
47
|
+
- A shared secret is derived through the agreement the toolkit captured, not one replaced afterwards. `crypto.diffieHellman` returns the traditional half of a hybrid secret outright, so a replacement supplies that half: driving `pki.hpke.decap` with one returning a fixed buffer derived a secret from the value the replacement chose. The post-quantum half, through `encapsulate` and `decapsulate`, had the same shape, as did the key generation that decides the ephemeral key an encapsulation is made under. `pki.hpke`, `pki.kem`, `pki.key`, `pki.cms` and `pki.webcrypto` all take these through operations captured at load.
|
|
48
|
+
- A content-encryption key, a MAC key, an IV, a nonce and a salt are drawn through the randomness the toolkit captured. Twenty-seven sites read it off the module handle when they needed it, so a replacement returning a value it already knows handed it every message those protect, and nothing downstream can tell a chosen key from a random one.
|
|
49
|
+
- An AEAD message is authenticated against the tag it carries. The tag is set and read through methods on the cipher object, which were reached through that object: a replacement decides which tag a ciphertext is checked against, so the verdict stops being a function of the message. The additional authenticated data, the padding mode, and the bytes a transform produces are reached the same way and are now taken through operations captured at load. The streaming paths in `pki.webcrypto` already did this; the one-shot encrypt and decrypt did not.
|
|
50
|
+
- A key-transport ciphertext is produced, and the secret inside one recovered, through the RSA transforms captured at load. A password-derived key-encryption key, an HKDF expansion, and the elliptic-curve point a scalar generates are the same shape: each returns a value rather than a claim about one, so a replacement returning a fixed value makes every password open the message or puts a point of its own choosing where a public key was expected.
|
|
51
|
+
- `pki.key.export` emits a PEM label that agrees with the bytes in the block. It asked the key its type once to choose the label and again to choose the encoding, and on a `CryptoKey` from node's own WebCrypto that is an accessor, so an accessor answering differently across those reads picked the two separately and a public key came back inside a `PRIVATE KEY` block. The type is read once and every decision in an export comes from that one answer.
|
|
52
|
+
- A digest is computed through the hash operation the toolkit captured, across every path that takes one: the certificate and CRL digests, the CMS message digests and countersignature imprints, the transparency-log identifiers, the PKCS#12 key derivation, the hash-based signature trees, the WebAuthn attestation comparisons and the HTTP Digest credential. `pki.scep`'s check that a named digest is available goes through the same operation, so the name it accepts is the name it would use.
|
|
53
|
+
- The RFC 7616 HTTP Digest credential is computed over the octets of the latin1 encoding the specification names, through one operation rather than a copy of the hash construction kept in the module. Three modules held their own copy of what the digest primitive already does; all three route through it now, and the input and output encodings are named at the call rather than defaulted.
|
|
54
|
+
|
|
7
55
|
## v0.8.60 — 2026-10-07
|
|
8
56
|
|
|
9
57
|
Build every list a parse or an encode reads from by defining its indexes, so an accessor inherited from the array prototype cannot take an element as it is added.
|
package/README.md
CHANGED
|
@@ -71,6 +71,13 @@ no transpilation.
|
|
|
71
71
|
var pki = require("@blamejs/pki");
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
An ESM caller can import the whole namespace object or any namespace by name:
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
import pki from "@blamejs/pki";
|
|
78
|
+
import { x509, cms, path, schema } from "@blamejs/pki";
|
|
79
|
+
```
|
|
80
|
+
|
|
74
81
|
## Quickstart
|
|
75
82
|
|
|
76
83
|
### Parse an X.509 certificate
|
package/index.js
CHANGED
|
@@ -73,8 +73,53 @@ var inspect = require("./lib/inspect");
|
|
|
73
73
|
var lint = require("./lib/lint");
|
|
74
74
|
var webauthn = require("./lib/webauthn");
|
|
75
75
|
|
|
76
|
+
/* Every value in the export object below is a bare identifier, and the ten `var`s here are what make
|
|
77
|
+
* that true. Node detects a CommonJS module's named exports with `cjs-module-lexer`, which follows
|
|
78
|
+
* `name: ident` and a shorthand and stops at anything else: a member expression, a nested object
|
|
79
|
+
* literal, a call. `version: constants.version` was the first entry, so it stopped the lexer before
|
|
80
|
+
* any namespace was seen and `import { x509 } from "@blamejs/pki"` failed for all 47 of them, with
|
|
81
|
+
* only `default`, `module.exports` and `version` reaching an ESM caller. Hoisting is the whole fix;
|
|
82
|
+
* it needs no build step and changes nothing a `require` caller sees. A new namespace added below
|
|
83
|
+
* must be a bare identifier, which a vector in `esm-named-exports.test.js` holds it to. */
|
|
84
|
+
var _version = constants.version;
|
|
85
|
+
var _identityNs = { match: identity.match };
|
|
86
|
+
var _cmsNs = {
|
|
87
|
+
verify: cms.verify, sign: cms.sign, countersign: cms.countersign, encrypt: cms.encrypt,
|
|
88
|
+
authenticate: cms.authenticate, decrypt: cms.decrypt, compress: cms.compress, decompress: cms.decompress,
|
|
89
|
+
digest: cms.digest, verifyDigest: cms.verifyDigest,
|
|
90
|
+
certsOnly: cms.certsOnly, parseCertsOnly: cms.parseCertsOnly, isCertsOnly: cms.isCertsOnly,
|
|
91
|
+
timestampImprint: cms.timestampImprint, attachTimestamp: cms.attachTimestamp,
|
|
92
|
+
};
|
|
93
|
+
var _cmpNs = {
|
|
94
|
+
build: cmp.build, transfer: cmp.transfer, wellKnownUrl: cmp.wellKnownUrl, verify: cmp.verify,
|
|
95
|
+
openKeyPackage: cmp.openKeyPackage, session: cmp.session,
|
|
96
|
+
};
|
|
97
|
+
var _keyNs = {
|
|
98
|
+
encrypt: key.encrypt, decrypt: key.decrypt, export: key.export, import: key.import,
|
|
99
|
+
generate: key.generate, publicFromPrivate: key.publicFromPrivate,
|
|
100
|
+
};
|
|
101
|
+
var _kemNs = { encapsulate: compositeKem.encapsulate, decapsulate: compositeKem.decapsulate };
|
|
102
|
+
var _scepNs = {
|
|
103
|
+
build: scep.build, parse: scep.parse, getCACaps: scep.getCACaps, getCACert: scep.getCACert,
|
|
104
|
+
getNextCACert: scep.getNextCACert, enroll: scep.enroll, renew: scep.renew, getCert: scep.getCert,
|
|
105
|
+
getCrl: scep.getCrl, parseCapabilities: scep.parseCapabilities,
|
|
106
|
+
};
|
|
107
|
+
var _transportNs = { https: transport.https, peerChain: transport.peerChain };
|
|
108
|
+
var _lintNs = {
|
|
109
|
+
certificate: lint.certificate,
|
|
110
|
+
csr: lint.csr,
|
|
111
|
+
crl: lint.crl,
|
|
112
|
+
ocsp: lint.ocsp,
|
|
113
|
+
cms: lint.cms,
|
|
114
|
+
tsp: lint.tsp,
|
|
115
|
+
attrcert: lint.attrcert,
|
|
116
|
+
rules: lint.rules,
|
|
117
|
+
profiles: lint.profiles,
|
|
118
|
+
};
|
|
119
|
+
var _webcryptoNs = _webcryptoNamespace();
|
|
120
|
+
|
|
76
121
|
module.exports = {
|
|
77
|
-
version:
|
|
122
|
+
version: _version,
|
|
78
123
|
// `C` is the terse call-site alias; `constants` the discoverable name.
|
|
79
124
|
C: constants,
|
|
80
125
|
constants: constants,
|
|
@@ -97,7 +142,7 @@ module.exports = {
|
|
|
97
142
|
// `identity` is RFC 9525 service identity -- pki.identity.match says whether a certificate
|
|
98
143
|
// presents a name the client was trying to reach. A separate answer from path validation,
|
|
99
144
|
// which pki.path.validate gives; sec. 1.2 has an application need both.
|
|
100
|
-
identity:
|
|
145
|
+
identity: _identityNs,
|
|
101
146
|
// `ct` is RFC 6962 Certificate Transparency -- pki.ct.parseSctList decodes the
|
|
102
147
|
// SCT-list extension a certificate / OCSP response carries; the signature is
|
|
103
148
|
// surfaced raw for external verification (pki.ct.reconstructSignedData).
|
|
@@ -113,11 +158,7 @@ module.exports = {
|
|
|
113
158
|
// Curated, the way pki.cmp is: cms-verify also exports `setEngine`, the seam path-validate injects
|
|
114
159
|
// its path builder through. That is plumbing between two internal modules, not an operator verb,
|
|
115
160
|
// and exporting the module wholesale would put it on the public surface.
|
|
116
|
-
cms:
|
|
117
|
-
authenticate: cms.authenticate, decrypt: cms.decrypt, compress: cms.compress, decompress: cms.decompress,
|
|
118
|
-
digest: cms.digest, verifyDigest: cms.verifyDigest,
|
|
119
|
-
certsOnly: cms.certsOnly, parseCertsOnly: cms.parseCertsOnly, isCertsOnly: cms.isCertsOnly,
|
|
120
|
-
timestampImprint: cms.timestampImprint, attachTimestamp: cms.attachTimestamp },
|
|
161
|
+
cms: _cmsNs,
|
|
121
162
|
smime: smime,
|
|
122
163
|
// `cmc` interprets an RFC 5272 Full PKI Response into one terminal verdict;
|
|
123
164
|
// `pki.schema.cmc` is the decoder underneath it.
|
|
@@ -147,8 +188,7 @@ module.exports = {
|
|
|
147
188
|
// and pki.cmp.verify checks the protection on an incoming one. pki.cmp.openKeyPackage opens a
|
|
148
189
|
// centrally generated private key a CA delivered. Parsing lives at pki.schema.cmp.parse.
|
|
149
190
|
// setEngine is the @internal path-validate seam -- kept off the public surface.
|
|
150
|
-
cmp:
|
|
151
|
-
openKeyPackage: cmp.openKeyPackage, session: cmp.session },
|
|
191
|
+
cmp: _cmpNs,
|
|
152
192
|
// `crl` is the RFC 5280 sec. 5 CRL producing side -- pki.crl.sign builds and signs a CertificateList
|
|
153
193
|
// over any registry algorithm, pki.crl.verify checks a CRL signature through the one path-validation
|
|
154
194
|
// signature engine, and pki.crl.isRevoked looks a serial up. Parsing lives at pki.schema.crl.parse.
|
|
@@ -158,8 +198,7 @@ module.exports = {
|
|
|
158
198
|
// pki.key.generate / publicFromPrivate over every WebCrypto algorithm. Parsing lives at pki.schema.pkcs8.
|
|
159
199
|
// Curated: correspondsTo is the @internal check pki.cmp.session holds a delivered key and its
|
|
160
200
|
// certificate to each other with, and stays off the public surface.
|
|
161
|
-
key:
|
|
162
|
-
generate: key.generate, publicFromPrivate: key.publicFromPrivate },
|
|
201
|
+
key: _keyNs,
|
|
163
202
|
// `pkcs12` is the RFC 7292 / RFC 9579 PKCS#12 (.p12/.pfx) producing side -- pki.pkcs12.build assembles a
|
|
164
203
|
// password-integrity store (key/cert/crl/secret bags in an AuthenticatedSafe, shrouded keys + cert safes
|
|
165
204
|
// under PBES2, a classic HMAC or PBMAC1 MAC), and pki.pkcs12.verifyMac checks a store's MAC. Parsing lives
|
|
@@ -193,7 +232,7 @@ module.exports = {
|
|
|
193
232
|
// `kem` is composite ML-KEM key establishment (draft-ietf-lamps-pq-composite-kem):
|
|
194
233
|
// pki.kem.encapsulate / decapsulate over a post-quantum ML-KEM hybridized with a
|
|
195
234
|
// traditional RSA-OAEP / ECDH / X25519 / X448, mixed through the SHA3-256 combiner.
|
|
196
|
-
kem:
|
|
235
|
+
kem: _kemNs,
|
|
197
236
|
// `sigstore` verifies a Sigstore bundle (the npm --provenance artifact): a
|
|
198
237
|
// keyless Fulcio signature over a DSSE-wrapped in-toto SLSA attestation with a
|
|
199
238
|
// Rekor inclusion proof -- offline, zero-dep, against caller-pinned trust.
|
|
@@ -204,14 +243,14 @@ module.exports = {
|
|
|
204
243
|
// splitter, certs-only + serverkeygen validators over CMS, the enroll-attribute
|
|
205
244
|
// builders, the HTTP response classifier). est opens no socket; fail-closed.
|
|
206
245
|
est: est,
|
|
207
|
-
scep:
|
|
246
|
+
scep: _scepNs,
|
|
208
247
|
// `transport` is the shared fail-closed node:https transport the enrollment clients
|
|
209
248
|
// drive -- pki.transport.https(defaults) returns a transport(request) -> {status,
|
|
210
249
|
// headers, body}. The toolkit's sole socket choke point: explicit trust anchors,
|
|
211
250
|
// rejectUnauthorized always on, a TLS floor, a streaming response cap, and a timeout.
|
|
212
251
|
// Curated to the public `https` factory; the module's `isBlockedIp` classifier is an
|
|
213
252
|
// internal helper pki.path.build reuses (require the module), not a public surface.
|
|
214
|
-
transport:
|
|
253
|
+
transport: _transportNs,
|
|
215
254
|
// `jose` is the RFC 7515 Flattened JWS + RFC 7638 JWK-thumbprint layer: a strict
|
|
216
255
|
// base64url codec, a bounded duplicate-key-rejecting JSON reader, profiled
|
|
217
256
|
// sign/verify (ACME-outer / EAB-inner / keyChange-inner), and an alg registry
|
|
@@ -241,17 +280,7 @@ module.exports = {
|
|
|
241
280
|
// `lint` is named member by member rather than passed through, because the module also exports the
|
|
242
281
|
// build-time gate the authoring verbs call and the two readers its conformance vectors use. Those
|
|
243
282
|
// are internal plumbing, so they stay off the public namespace.
|
|
244
|
-
lint:
|
|
245
|
-
certificate: lint.certificate,
|
|
246
|
-
csr: lint.csr,
|
|
247
|
-
crl: lint.crl,
|
|
248
|
-
ocsp: lint.ocsp,
|
|
249
|
-
cms: lint.cms,
|
|
250
|
-
tsp: lint.tsp,
|
|
251
|
-
attrcert: lint.attrcert,
|
|
252
|
-
rules: lint.rules,
|
|
253
|
-
profiles: lint.profiles,
|
|
254
|
-
},
|
|
283
|
+
lint: _lintNs,
|
|
255
284
|
// `webauthn` verifies a W3C WebAuthn / passkey attestation -- pki.webauthn.verify
|
|
256
285
|
// checks the attestation-statement signature + each format's structural bindings
|
|
257
286
|
// (packed / tpm / android-key / apple / fido-u2f / none) and surfaces the x5c chain
|
|
@@ -263,7 +292,7 @@ module.exports = {
|
|
|
263
292
|
// A ready W3C Crypto instance (globalThis.crypto shape) with the classes for
|
|
264
293
|
// constructing more attached under the same namespace (pki.webcrypto.CryptoKey,
|
|
265
294
|
// .SubtleCrypto, .Crypto, .WebCryptoError). PQC-first, classical-capable, zero-dep.
|
|
266
|
-
webcrypto:
|
|
295
|
+
webcrypto: _webcryptoNs,
|
|
267
296
|
};
|
|
268
297
|
|
|
269
298
|
function _webcryptoNamespace() {
|
package/lib/acme.js
CHANGED
|
@@ -1796,7 +1796,7 @@ function _parseLinkValue(raw) {
|
|
|
1796
1796
|
return { uri: uri, rel: rel, anchor: anchor, relSeen: relSeen };
|
|
1797
1797
|
}
|
|
1798
1798
|
function _parseLinkRelation(headers, base, relName, opts) {
|
|
1799
|
-
opts = opts ||
|
|
1799
|
+
opts = opts || intrinsic.create(null);
|
|
1800
1800
|
var raw = null;
|
|
1801
1801
|
var hNames = guard.identifier.optionNames(headers);
|
|
1802
1802
|
for (var hi = 0; hi < hNames.length; hi++) {
|
|
@@ -2047,11 +2047,11 @@ function client(directoryUrl, opts) {
|
|
|
2047
2047
|
|
|
2048
2048
|
function _send(method, url, headers, body) { return _sendFollowing(method, url, headers, body, 0); }
|
|
2049
2049
|
function _sendFollowing(method, url, headers, body, redirects) {
|
|
2050
|
-
var reqHeaders = Object.assign({ "user-agent": CLIENT_USER_AGENT }, headers ||
|
|
2050
|
+
var reqHeaders = Object.assign({ "user-agent": CLIENT_USER_AGENT }, headers || intrinsic.create(null));
|
|
2051
2051
|
return guard.async.deferred(function () {
|
|
2052
2052
|
return guard.async.invoked(transport, [{ method: method, url: url, headers: reqHeaders, body: body, tls: _tlsFor(url), proxy: budgets.proxy, timeout: budgets.timeout, maxResponseBytes: budgets.maxResponseBytes }], E, "acme/bad-input", "opts.transport");
|
|
2053
2053
|
}).then(function (res) {
|
|
2054
|
-
res = res ||
|
|
2054
|
+
res = res || intrinsic.create(null);
|
|
2055
2055
|
/** @internal The transport is a caller-supplied option, so its response is a caller object: the body
|
|
2056
2056
|
* and the status are read ONCE here and every check and the returned value read those locals. Read
|
|
2057
2057
|
* per use, the body whose length was capped need not be the body handed back, which is how a
|
|
@@ -2145,7 +2145,7 @@ function client(directoryUrl, opts) {
|
|
|
2145
2145
|
|
|
2146
2146
|
function _newAccount(payloadOpts) {
|
|
2147
2147
|
return _resource("newAccount").then(function (url) {
|
|
2148
|
-
return _post(url, newAccount, payloadOpts ||
|
|
2148
|
+
return _post(url, newAccount, payloadOpts || intrinsic.create(null), "jwk", undefined, [200, 201]).then(function (res) {
|
|
2149
2149
|
var loc = res.headers["location"];
|
|
2150
2150
|
if (!_isString(loc)) throw E("acme/no-account-url", "newAccount did not return an account URL in a Location header (RFC 8555 sec. 7.3)");
|
|
2151
2151
|
var account = validate("account", _json(res));
|
|
@@ -2156,7 +2156,7 @@ function client(directoryUrl, opts) {
|
|
|
2156
2156
|
}
|
|
2157
2157
|
function _newOrder(payloadOpts) {
|
|
2158
2158
|
return _resource("newOrder").then(function (url) {
|
|
2159
|
-
return _post(url, newOrder, payloadOpts ||
|
|
2159
|
+
return _post(url, newOrder, payloadOpts || intrinsic.create(null), "kid", undefined, [201]).then(function (res) {
|
|
2160
2160
|
var loc = res.headers["location"];
|
|
2161
2161
|
if (!_isString(loc)) throw E("acme/no-order-url", "newOrder did not return an order URL in a Location header (RFC 8555 sec. 7.4)");
|
|
2162
2162
|
return { order: validate("order", _json(res)), url: _resolveLocation(loc, url) };
|
package/lib/asn1-der.js
CHANGED
|
@@ -210,7 +210,7 @@ function _asBuffer(input, who) {
|
|
|
210
210
|
* node.tagNumber === pki.asn1.TAGS.SEQUENCE;
|
|
211
211
|
*/
|
|
212
212
|
function decode(input, opts) {
|
|
213
|
-
opts = opts ||
|
|
213
|
+
opts = opts || intrinsic.create(null);
|
|
214
214
|
var buf = _asBuffer(input, "decode");
|
|
215
215
|
var maxBytes = guard.limits.cap(opts.maxBytes, "maxBytes", constants.LIMITS.DER_MAX_BYTES);
|
|
216
216
|
var maxDepth = guard.limits.depthCap(opts.maxDepth, "maxDepth", constants.LIMITS.DER_MAX_DEPTH);
|
package/lib/attrcert-sign.js
CHANGED
|
@@ -448,7 +448,7 @@ function _emitNamedExtension(out, seen, k, val, ctx) {
|
|
|
448
448
|
_push(out,_b.ext(eOid, meta.critical, valueDer));
|
|
449
449
|
}
|
|
450
450
|
function _buildExtensions(extSpec, ctx) {
|
|
451
|
-
if (extSpec == null) extSpec =
|
|
451
|
+
if (extSpec == null) extSpec = intrinsic.create(null);
|
|
452
452
|
if (Array.isArray(extSpec)) {
|
|
453
453
|
if (!extSpec.length) return null;
|
|
454
454
|
var seenA = intrinsic.create(null);
|
|
@@ -567,7 +567,7 @@ function _sign(spec, issuer, opts) {
|
|
|
567
567
|
guard.identifier.assertKnownKeys(opts, KNOWN_SIGN_OPTS, _err, "attrcert/bad-input", "unknown pki.attrcert.sign option (accepted: pem, pss, digestAlgorithm): ");
|
|
568
568
|
if (!spec || typeof spec !== "object" || Buffer.isBuffer(spec)) throw _err("attrcert/bad-input", "the attribute-certificate spec must be an object");
|
|
569
569
|
guard.identifier.assertKnownKeys(spec, KNOWN_SPEC_KEYS, _err, "attrcert/bad-input", "unknown spec field ");
|
|
570
|
-
issuer = issuer ||
|
|
570
|
+
issuer = issuer || intrinsic.create(null);
|
|
571
571
|
if (issuer.key == null) throw _err("attrcert/bad-input", "a signing key (issuer.key, the AA's WebCrypto CryptoKey or PKCS#8 private key DER/PEM) is required");
|
|
572
572
|
|
|
573
573
|
var issuerDnSpec, aaSpki, aaSki = null;
|
package/lib/cbor-det.js
CHANGED
|
@@ -36,6 +36,9 @@
|
|
|
36
36
|
* leaf readers over its node tree.
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
|
+
/** @internal The record constructor, captured at load, for the reason the same capture in
|
|
40
|
+
* `lib/inspect.js` gives: arming this module counts 66 live runtime reads. */
|
|
41
|
+
var _create = Object.create;
|
|
39
42
|
var constants = require("./constants");
|
|
40
43
|
var frameworkError = require("./framework-error");
|
|
41
44
|
var asn1 = require("./asn1-der");
|
|
@@ -241,7 +244,7 @@ function _decodeItem(buf, start, limit, depth, maxD, rules, state) {
|
|
|
241
244
|
* pki.cbor.read.uint(node.children[0]); // 1n
|
|
242
245
|
*/
|
|
243
246
|
function decode(input, opts) {
|
|
244
|
-
opts = opts ||
|
|
247
|
+
opts = opts || _create(null);
|
|
245
248
|
var buf = _asBuffer(input, "decode");
|
|
246
249
|
var maxBytes = guard.limits.cap(opts.maxBytes, "maxBytes", constants.LIMITS.CBOR_MAX_BYTES);
|
|
247
250
|
var maxDepth = guard.limits.depthCap(opts.maxDepth, "maxDepth", constants.LIMITS.CBOR_MAX_DEPTH);
|
package/lib/cmc-build.js
CHANGED
|
@@ -470,8 +470,8 @@ function identityProofV2(secret, reqSequenceBytes, identity) {
|
|
|
470
470
|
_ownSecret(Buffer.from(identity, "utf8"), owned),
|
|
471
471
|
]), owned);
|
|
472
472
|
}
|
|
473
|
-
key =
|
|
474
|
-
var witness =
|
|
473
|
+
key = guard.crypto.digest("sha256", material);
|
|
474
|
+
var witness = guard.crypto.hmac("sha256", key, reqSequenceBytes);
|
|
475
475
|
return b.sequence([algId(OID_SHA256), algId(OID_HMAC_SHA256), b.octetString(witness)]);
|
|
476
476
|
} finally {
|
|
477
477
|
if (key) guard.secret.zeroize(key, CmcError, "cmc/bad-input", "the Identity Proof MAC key");
|
|
@@ -541,7 +541,7 @@ function _provesRequestKey(opening, outgoing) {
|
|
|
541
541
|
/** @internal Keys of different types are never the same key, and comparing them through the
|
|
542
542
|
* runtime's equality leaves a fault queued in the crypto library that surfaces on the next
|
|
543
543
|
* unrelated key import in the process, so the type decides that case first. */
|
|
544
|
-
if (requested
|
|
544
|
+
if (guard.crypto.keyType(requested) !== guard.crypto.keyType(opening.key)) return false;
|
|
545
545
|
return intrinsic.keyEquals(requested, opening.key);
|
|
546
546
|
}
|
|
547
547
|
/** @internal A key the runtime engine cannot read has no KeyObject to compare, and a composite
|
|
@@ -607,7 +607,7 @@ async function _recoverProofValue(decoded, recipient, witnessHash) {
|
|
|
607
607
|
continue; // allow:swallow-unverified this recipient is not addressed to the supplied key; the witness decides
|
|
608
608
|
}
|
|
609
609
|
var candidate = opened.content;
|
|
610
|
-
var computed =
|
|
610
|
+
var computed = guard.crypto.digest(witnessHash, candidate);
|
|
611
611
|
var matches = computed.length === decoded.witness.length &&
|
|
612
612
|
guard.crypto.constantTimeEqual(computed, decoded.witness);
|
|
613
613
|
guard.secret.zeroize(computed, CmcError, "cmc/bad-input", "the computed witness");
|
|
@@ -674,7 +674,7 @@ async function answerPopChallenge(popChallenge, ids) {
|
|
|
674
674
|
* in the challenge at all: neither side re-encodes it. */
|
|
675
675
|
macKey = _bufferFrom(proofValue.length > POP_MAC_KEY_MAX_BYTES
|
|
676
676
|
? _subarray(proofValue, 0, POP_MAC_KEY_MAX_BYTES) : proofValue);
|
|
677
|
-
var thePOP =
|
|
677
|
+
var thePOP = guard.crypto.hmac(macHash, macKey, decoded.requestBytes);
|
|
678
678
|
return { algBytes: decoded.thePOPAlgID.bytes, thePOP: thePOP, ids: ids,
|
|
679
679
|
recipientSpki: await _openingPublicKey(recipient, recovered.recipientType),
|
|
680
680
|
challengedRequest: _requestIdentity(decoded.request) };
|
|
@@ -688,13 +688,18 @@ async function answerPopChallenge(popChallenge, ids) {
|
|
|
688
688
|
|
|
689
689
|
function popLinkWitnessV2(secret, R) {
|
|
690
690
|
_assertSecret(secret, "a POP Link Witness V2 control");
|
|
691
|
-
|
|
691
|
+
/** @internal The secret's octets, held so the finally can wipe them. Handing the string and an
|
|
692
|
+
* encoding to a digest instead leaves the copy inside the provider, where nothing clears it. */
|
|
693
|
+
var material = _bufferFrom(secret, "utf8");
|
|
694
|
+
var key = null;
|
|
692
695
|
try {
|
|
696
|
+
key = guard.crypto.digest("sha256", material);
|
|
693
697
|
/** @internal Inside the try, so a MAC that faults on its message still wipes the key above it. */
|
|
694
|
-
var witness =
|
|
698
|
+
var witness = guard.crypto.hmac("sha256", key, R);
|
|
695
699
|
return b.sequence([algId(OID_SHA256), algId(OID_HMAC_SHA256), b.octetString(witness)]);
|
|
696
700
|
} finally {
|
|
697
|
-
guard.secret.zeroize(
|
|
701
|
+
guard.secret.zeroize(material, CmcError, "cmc/bad-input", "the POP Link secret");
|
|
702
|
+
if (key !== null) guard.secret.zeroize(key, CmcError, "cmc/bad-input", "the POP Link MAC key");
|
|
698
703
|
}
|
|
699
704
|
}
|
|
700
705
|
|
|
@@ -707,7 +712,7 @@ function build(spec, signer, opts) {
|
|
|
707
712
|
}
|
|
708
713
|
|
|
709
714
|
async function _build(spec, signer, opts) {
|
|
710
|
-
opts = opts ||
|
|
715
|
+
opts = opts || intrinsic.create(null);
|
|
711
716
|
if (!spec || typeof spec !== "object" || Array.isArray(spec) || Buffer.isBuffer(spec)) {
|
|
712
717
|
throw E("cmc/bad-input", "the CMC request spec must be an object");
|
|
713
718
|
}
|
|
@@ -935,7 +940,7 @@ async function _build(spec, signer, opts) {
|
|
|
935
940
|
if (typeof rBytes !== "number" || !Number.isInteger(rBytes) || rBytes < 1) {
|
|
936
941
|
throw E("cmc/bad-input", "popLinkRandomBytes must be a positive integer");
|
|
937
942
|
}
|
|
938
|
-
var R =
|
|
943
|
+
var R = guard.crypto.randomBytes(rBytes);
|
|
939
944
|
_push(controls,encodeControl(OID_POP_LINK_RANDOM, [b.octetString(R)], ids));
|
|
940
945
|
_push(controls,encodeControl(O("id-cmc-popLinkWitnessV2"),
|
|
941
946
|
[popLinkWitnessV2(spec.popLink.secret, R)], ids));
|
package/lib/cmp-build.js
CHANGED
|
@@ -856,7 +856,7 @@ function build(message, opts) {
|
|
|
856
856
|
}
|
|
857
857
|
|
|
858
858
|
async function _build(message, opts) {
|
|
859
|
-
opts = opts ||
|
|
859
|
+
opts = opts || intrinsic.create(null);
|
|
860
860
|
if (!message || typeof message !== "object" || Buffer.isBuffer(message)) throw _err("cmp/bad-input", "the PKIMessage spec must be an object { header, body }");
|
|
861
861
|
guard.identifier.assertKnownKeys(message, KNOWN_MESSAGE_KEYS, _err, "cmp/bad-input", function (k) {
|
|
862
862
|
return "unknown message field " + JSON.stringify(k) + " (a message carries only header + body)";
|
|
@@ -1020,7 +1020,7 @@ function _tlsForTransfer(opts) {
|
|
|
1020
1020
|
* `Symbol.split` is part of that operation. The token decides which parser a response body is
|
|
1021
1021
|
* handed to. */
|
|
1022
1022
|
function _ctypeToken(headers) {
|
|
1023
|
-
var raw = intrinsic.String((headers ||
|
|
1023
|
+
var raw = intrinsic.String((headers || intrinsic.create(null))["content-type"] || "");
|
|
1024
1024
|
var parts = intrinsic.splitChar(raw, 59);
|
|
1025
1025
|
return intrinsic.toLowerCase(_strTrim(parts.length ? parts[0] : ""));
|
|
1026
1026
|
}
|
|
@@ -1067,7 +1067,7 @@ function _assertTransport(opts) {
|
|
|
1067
1067
|
}
|
|
1068
1068
|
|
|
1069
1069
|
function _transfer(url, message, opts) {
|
|
1070
|
-
opts = opts ||
|
|
1070
|
+
opts = opts || intrinsic.create(null);
|
|
1071
1071
|
guard.identifier.assertKnownKeys(opts, KNOWN_TRANSFER_OPTS, _err, "cmp/bad-input", "unknown opts field ");
|
|
1072
1072
|
/** @internal Read once, at the door, and carried from here. Asking again would let an option that
|
|
1073
1073
|
* answers differently on each read hand back something other than the value that was checked. */
|
|
@@ -1095,7 +1095,7 @@ function _transfer(url, message, opts) {
|
|
|
1095
1095
|
headers["content-type"] = PKIXCMP;
|
|
1096
1096
|
var tls = _tlsForTransfer(opts);
|
|
1097
1097
|
return guard.async.invoked(transport, [{ method: "POST", url: parsedUrl.href, headers: headers, body: der, tls: tls, proxy: proxy, timeout: timeout, maxResponseBytes: maxResponseBytes }], _err, "cmp/bad-input", "opts.transport").then(function (res) {
|
|
1098
|
-
res = res ||
|
|
1098
|
+
res = res || intrinsic.create(null);
|
|
1099
1099
|
var h = guard.header.lowerCased(res.headers);
|
|
1100
1100
|
var body = guard.bytes.isByteSource(res.body)
|
|
1101
1101
|
? guard.bytes.source(res.body, CmpError, "cmp/bad-response", "the CMP response body")
|
|
@@ -1136,7 +1136,7 @@ function _beforeQueryOrFragment(s) {
|
|
|
1136
1136
|
}
|
|
1137
1137
|
|
|
1138
1138
|
function wellKnownUrl(base, opts) {
|
|
1139
|
-
opts = opts ||
|
|
1139
|
+
opts = opts || intrinsic.create(null);
|
|
1140
1140
|
guard.identifier.assertKnownKeys(opts, KNOWN_WELLKNOWN_OPTS, _err, "cmp/bad-input", function (k) {
|
|
1141
1141
|
return "unknown wellKnownUrl option " + JSON.stringify(k) + " (expected label / operation)";
|
|
1142
1142
|
});
|
package/lib/cmp-session.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
// @internal
|
|
5
5
|
// lib/cmp-build.js; this file adds the @primitive pki.cmp.session block and re-exports the cmp message
|
|
6
6
|
|
|
7
|
+
/** @internal The record constructor, captured at load, for the reason the same capture in
|
|
8
|
+
* `lib/inspect.js` gives: arming this module counts 174 live runtime reads. */
|
|
9
|
+
var _create = Object.create;
|
|
7
10
|
var cmp = require("./cmp-verify");
|
|
8
11
|
var asn1 = require("./asn1-der");
|
|
9
12
|
var oid = require("./oid");
|
|
@@ -430,7 +433,7 @@ function _certConfHash(certDer) {
|
|
|
430
433
|
* if (answer.outcome === "answered" && answer.present) { var caCerts = answer.value; }
|
|
431
434
|
*/
|
|
432
435
|
function session(opts) {
|
|
433
|
-
if (opts == null) opts =
|
|
436
|
+
if (opts == null) opts = _create(null);
|
|
434
437
|
guard.identifier.assertPlainRecord(opts, _err, "cmp/bad-input", "opts");
|
|
435
438
|
guard.identifier.optionsObject(opts, _err, "cmp/bad-input", "opts");
|
|
436
439
|
guard.identifier.assertKnownKeys(opts, KNOWN_SESSION_OPTS, _err, "cmp/bad-input", "unknown session opts field ");
|
|
@@ -668,7 +671,7 @@ function session(opts) {
|
|
|
668
671
|
}
|
|
669
672
|
|
|
670
673
|
function _classify(verdict) {
|
|
671
|
-
var body = verdict.body ||
|
|
674
|
+
var body = verdict.body || _create(null);
|
|
672
675
|
var arm = body.arm;
|
|
673
676
|
if (arm === expectedRespArm && txnKind === "revoke") return _classifyRevRep(body);
|
|
674
677
|
if (arm === expectedRespArm && txnKind === "info") return _classifyGenRep(body);
|
|
@@ -734,7 +737,7 @@ function session(opts) {
|
|
|
734
737
|
}
|
|
735
738
|
|
|
736
739
|
function _classifyRevRep(body) {
|
|
737
|
-
var d = body.decoded ||
|
|
740
|
+
var d = body.decoded || _create(null);
|
|
738
741
|
var list = d.status || [];
|
|
739
742
|
if (list.length !== 1) {
|
|
740
743
|
return { state: "unexpected", reason: "an rp answering a revocation must carry exactly one status (RFC 9483 sec. 4.2); got " + list.length };
|
package/lib/cmp-verify.js
CHANGED
|
@@ -811,7 +811,7 @@ function _fixTime(v) {
|
|
|
811
811
|
return new _Date(guard.time.instantOf(v));
|
|
812
812
|
}
|
|
813
813
|
function _fixVerifyOptions(opts, made) {
|
|
814
|
-
if (opts == null) opts =
|
|
814
|
+
if (opts == null) opts = _create(null);
|
|
815
815
|
opts = guard.identifier.optionsObject(opts, _err, "cmp/bad-input", "pki.cmp.verify options");
|
|
816
816
|
guard.identifier.assertKnownKeys(opts, KNOWN_VERIFY_OPTS, _err, "cmp/bad-input", "unknown opts field ");
|
|
817
817
|
var f = {
|
package/lib/cms-compress.js
CHANGED
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
// @internal
|
|
5
5
|
// @module pki.cms + the @primitive blocks live in cms-verify.js, which re-exports these (the
|
|
6
6
|
|
|
7
|
+
/** @internal The record constructor, captured at load, for the reason the same capture in
|
|
8
|
+
* `lib/inspect.js` gives: arming this module counts 5 live runtime reads. */
|
|
9
|
+
var _create = Object.create;
|
|
7
10
|
var zlib = require("zlib");
|
|
8
11
|
var oid = require("./oid");
|
|
9
12
|
var schemaCms = require("./schema-cms");
|
|
@@ -21,7 +24,7 @@ var OID_ZLIB = O("id-alg-zlibCompress");
|
|
|
21
24
|
var NULL_PARAMS_DER = Buffer.from([0x05, 0x00]);
|
|
22
25
|
|
|
23
26
|
async function compress(content, opts) {
|
|
24
|
-
opts = opts ||
|
|
27
|
+
opts = opts || _create(null);
|
|
25
28
|
var raw = guard.bytes.source(content, CmsError, "cms/bad-input", "content");
|
|
26
29
|
var level = opts.level;
|
|
27
30
|
if (level !== undefined && (typeof level !== "number" || !isFinite(level) || Math.floor(level) !== level)) throw _err("cms/bad-input", "opts.level must be an integer");
|
|
@@ -41,7 +44,7 @@ async function compress(content, opts) {
|
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
async function decompress(input, opts) {
|
|
44
|
-
opts = opts ||
|
|
47
|
+
opts = opts || _create(null);
|
|
45
48
|
var cap = C.LIMITS.COMPRESS_MAX_BYTES;
|
|
46
49
|
if (opts.maxOutputBytes !== undefined) {
|
|
47
50
|
var mo = opts.maxOutputBytes;
|
package/lib/cms-decrypt.js
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
// @internal
|
|
5
5
|
// @primitive pki.cms.decrypt block live in cms-verify.js, which re-exports this function.
|
|
6
6
|
|
|
7
|
-
var nodeCrypto = require("crypto");
|
|
8
7
|
var asn1 = require("./asn1-der");
|
|
9
8
|
var oid = require("./oid");
|
|
10
9
|
var x509 = require("./schema-x509");
|
|
@@ -35,7 +34,7 @@ var CONTENT_KEYBITS = pbes2.CONTENT_KEYBITS;
|
|
|
35
34
|
var CONTENT_MODE = pbes2.CONTENT_MODE;
|
|
36
35
|
|
|
37
36
|
async function decrypt(input, keyMaterial, opts) {
|
|
38
|
-
opts = opts ||
|
|
37
|
+
opts = opts || intrinsic.create(null);
|
|
39
38
|
if (keyMaterial == null || typeof keyMaterial !== "object") throw _err("cms/bad-input", "decrypt requires a key-material object");
|
|
40
39
|
/** @internal Read on every route, not only the one that applies it, and read before the message
|
|
41
40
|
* is derived: deriving one runs accessors on a parsed input, which is the caller's own code and
|
|
@@ -67,7 +66,7 @@ function _originFields(res, authenticatedBy, originatorInfo) {
|
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
async function decryptEnvelopedData(parsed, keyMaterial, opts, contentTypeName) {
|
|
70
|
-
opts = opts ||
|
|
69
|
+
opts = opts || intrinsic.create(null);
|
|
71
70
|
if (keyMaterial == null || typeof keyMaterial !== "object") throw _err("cms/bad-input", "decrypt requires a key-material object");
|
|
72
71
|
var ct = contentTypeName || "envelopedData";
|
|
73
72
|
var recips = parsed.recipientInfos || [];
|
|
@@ -235,8 +234,8 @@ async function _ktriCek(ri, km) {
|
|
|
235
234
|
return Buffer.from(await subtle.decrypt({ name: "RSA-OAEP" }, pub, ri.encryptedKey));
|
|
236
235
|
}
|
|
237
236
|
if (kea.oid === O("rsaEncryption")) {
|
|
238
|
-
var keyObj =
|
|
239
|
-
try { return
|
|
237
|
+
var keyObj = guard.crypto.privateKey({ key: k.der, format: "der", type: "pkcs8" });
|
|
238
|
+
try { return guard.crypto.privateDecrypt({ key: keyObj, padding: guard.crypto.padding.RSA_PKCS1 }, ri.encryptedKey); }
|
|
240
239
|
catch (_e) {
|
|
241
240
|
return null;
|
|
242
241
|
}
|
|
@@ -339,7 +338,7 @@ async function _pwriCek(ri, km, opts) {
|
|
|
339
338
|
var iv = asn1.read.octetString(inner.children[1]);
|
|
340
339
|
var pw = pbes2.passwordBytesOwned(km.password, _err, "cms");
|
|
341
340
|
var kek;
|
|
342
|
-
try { kek =
|
|
341
|
+
try { kek = guard.crypto.pbkdf2(pw.bytes, pb.salt, pb.iterations, innerBits / 8, pb.prfNode); }
|
|
343
342
|
finally { if (pw.owned) guard.secret.zeroize(pw.bytes, CmsError, "cms/bad-input", "the password encoding"); }
|
|
344
343
|
try {
|
|
345
344
|
return _pwriUnwrap(kek, ri.encryptedKey, iv, innerBits);
|
|
@@ -421,7 +420,7 @@ async function _openContent(parsed, eci, cek, ct, streamMode) {
|
|
|
421
420
|
}
|
|
422
421
|
if (eci.encryptedContent == null) throw _err("cms/no-encrypted-content", "the message has no encryptedContent (detached; supply it out of band)");
|
|
423
422
|
var substitute = null;
|
|
424
|
-
if (cek == null || cek.length !== keyBits / 8) { substitute =
|
|
423
|
+
if (cek == null || cek.length !== keyBits / 8) { substitute = guard.crypto.randomBytes(keyBits / 8); cek = substitute; }
|
|
425
424
|
try {
|
|
426
425
|
if (ct === "authEnvelopedData") {
|
|
427
426
|
var aad = parsed.authAttrsBytes != null ? _explicitSetOf(parsed.authAttrsBytes) : Buffer.alloc(0);
|
|
@@ -461,9 +460,9 @@ async function _openContent(parsed, eci, cek, ct, streamMode) {
|
|
|
461
460
|
}
|
|
462
461
|
function _gcmOpen(cek, nonce, ct, tag, aad, keyBits, icvLen) {
|
|
463
462
|
if (!tag || tag.length !== icvLen) throw _fail();
|
|
464
|
-
var d =
|
|
465
|
-
|
|
466
|
-
if (aad && aad.length)
|
|
463
|
+
var d = guard.crypto.decipher("aes-" + keyBits + "-gcm", cek, nonce, { authTagLength: icvLen });
|
|
464
|
+
guard.crypto.decipherSetAuthTag(d, tag);
|
|
465
|
+
if (aad && aad.length) guard.crypto.transformSetAAD(d, aad);
|
|
467
466
|
return guard.secret.cipherFinish(d, ct, CmsError, "cms/decrypt-failed", "the recovered content");
|
|
468
467
|
}
|
|
469
468
|
|
|
@@ -543,7 +542,7 @@ async function _decryptPbes2(parsed, eci, km, opts) {
|
|
|
543
542
|
if (!keyBits) throw _err("cms/unsupported-algorithm", "unsupported PBES2 content cipher " + encOid);
|
|
544
543
|
var pwE = pbes2.passwordBytesOwned(km.password, _err, "cms");
|
|
545
544
|
var key;
|
|
546
|
-
try { key =
|
|
545
|
+
try { key = guard.crypto.pbkdf2(pwE.bytes, pb.salt, pb.iterations, keyBits / 8, pb.prfNode); }
|
|
547
546
|
finally { if (pwE.owned) guard.secret.zeroize(pwE.bytes, CmsError, "cms/bad-input", "the password encoding"); }
|
|
548
547
|
try { return _originFields({ content: opts.stream === true ? _cbcStream(key, iv, eci.encryptedContent, keyBits) : pbes2.cbcDecrypt(key, iv, eci.encryptedContent, keyBits, CmsError, "cms/decrypt-failed"), contentType: eci.contentType, contentTypeName: oid.name(eci.contentType) || eci.contentType, recipientType: "password", recipientIndex: -1, contentEncryptionAlgorithm: oid.name(encOid) || encOid, authenticated: false }, null, null); }
|
|
549
548
|
catch (_e) { throw _fail(); }
|
|
@@ -588,13 +587,13 @@ function _pwriUnwrap(kek, wrapped, iv, keyBits) {
|
|
|
588
587
|
var n = wrapped.length;
|
|
589
588
|
var lastDec = null, pass1 = null, body = null;
|
|
590
589
|
try {
|
|
591
|
-
var ecb =
|
|
590
|
+
var ecb = guard.crypto.decipher("aes-" + keyBits + "-ecb", kek, Buffer.alloc(0)); guard.crypto.transformNoPadding(ecb);
|
|
592
591
|
lastDec = guard.secret.cipherFinish(ecb, wrapped.subarray(n - blk), CmsError, "cms/decrypt-failed", "the recovered CBC last block");
|
|
593
592
|
var iv2 = Buffer.alloc(blk);
|
|
594
593
|
for (var i = 0; i < blk; i++) iv2[i] = lastDec[i] ^ wrapped[n - 2 * blk + i];
|
|
595
|
-
var d1 =
|
|
594
|
+
var d1 = guard.crypto.decipher(alg, kek, iv2); guard.crypto.transformNoPadding(d1);
|
|
596
595
|
pass1 = guard.secret.cipherFinish(d1, wrapped, CmsError, "cms/decrypt-failed", "the PWRI first-pass plaintext");
|
|
597
|
-
var d2 =
|
|
596
|
+
var d2 = guard.crypto.decipher(alg, kek, iv); guard.crypto.transformNoPadding(d2);
|
|
598
597
|
body = guard.secret.cipherFinish(d2, pass1, CmsError, "cms/decrypt-failed", "the PWRI plaintext block");
|
|
599
598
|
var count = body[0];
|
|
600
599
|
if (count < 1 || count + 4 > body.length) throw _fail();
|
|
@@ -670,8 +669,9 @@ function _ecCurveFromCert(cert) {
|
|
|
670
669
|
function _ecCurveFromPrivateKey(keyDer) {
|
|
671
670
|
var spki;
|
|
672
671
|
try {
|
|
673
|
-
spki =
|
|
674
|
-
.
|
|
672
|
+
spki = guard.crypto.exportKey(
|
|
673
|
+
guard.crypto.publicKey({ key: keyDer, format: "der", type: "pkcs8" }),
|
|
674
|
+
{ format: "der", type: "spki" });
|
|
675
675
|
} catch (_e) { return null; } // allow:swallow-unverified a key this runtime cannot read names no curve
|
|
676
676
|
var alg = asn1.decode(spki).children[0];
|
|
677
677
|
if (asn1.read.oid(alg.children[0]) !== O("ecPublicKey") || alg.children.length < 2) return null;
|
|
@@ -756,7 +756,7 @@ async function _verifyAuthenticatedData(parsed, km, opts, requireProtection) {
|
|
|
756
756
|
_assertSupported(candidates[ci].ri, km);
|
|
757
757
|
var macKey = await _acquireCek(candidates[ci].ri, km, opts);
|
|
758
758
|
var macSubstitute = null;
|
|
759
|
-
if (macKey == null || macKey.length < MAC_KEY_MIN_OCTETS) macSubstitute =
|
|
759
|
+
if (macKey == null || macKey.length < MAC_KEY_MIN_OCTETS) macSubstitute = guard.crypto.randomBytes(MAC_KEY_MIN_OCTETS);
|
|
760
760
|
try {
|
|
761
761
|
var key = await subtle.importKey("raw", macSubstitute || macKey, { name: "HMAC", hash: hash }, false, ["verify"]);
|
|
762
762
|
if (!(await _subtleVerify(subtle, { name: "HMAC" }, key, Buffer.from(parsed.mac), preimage))) throw _fail();
|