Skip to content

Commit 88cbceb

Browse files
aduh95bengl
authored andcommitted
crypto: expose Web Crypto API on the global scope
PR-URL: #41938 Refs: https://developer.mozilla.org/en-US/docs/Web/API/crypto_property Refs: #41782 Refs: https://w3c.github.io/webcrypto Reviewed-By: James M Snell <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Filip Skokan <[email protected]>
1 parent 02548ad commit 88cbceb

14 files changed

+160
-0
lines changed

.eslintrc.js

+4
Original file line numberDiff line numberDiff line change
@@ -338,5 +338,9 @@ module.exports = {
338338
Headers: 'readable',
339339
Request: 'readable',
340340
Response: 'readable',
341+
crypto: 'readable',
342+
Crypto: 'readable',
343+
CryptoKey: 'readable',
344+
SubtleCrypto: 'readable',
341345
},
342346
};

doc/api/cli.md

+10
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,14 @@ added: v17.5.0
288288

289289
Enable experimental support for the [Fetch API][].
290290

291+
### `--experimental-global-webcrypto`
292+
293+
<!-- YAML
294+
added: REPLACEME
295+
-->
296+
297+
Expose the [Web Crypto API][] on the global scope.
298+
291299
### `--experimental-import-meta-resolve`
292300

293301
<!-- YAML
@@ -1578,6 +1586,7 @@ Node.js options that are allowed are:
15781586
* `--enable-source-maps`
15791587
* `--experimental-abortcontroller`
15801588
* `--experimental-fetch`
1589+
* `--experimental-global-webcrypto`
15811590
* `--experimental-import-meta-resolve`
15821591
* `--experimental-json-modules`
15831592
* `--experimental-loader`
@@ -1980,6 +1989,7 @@ $ node --max-old-space-size=1536 index.js
19801989
[Source Map]: https://sourcemaps.info/spec.html
19811990
[Subresource Integrity]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
19821991
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
1992+
[Web Crypto API]: webcrypto.md
19831993
[`"type"`]: packages.md#type
19841994
[`--cpu-prof-dir`]: #--cpu-prof-dir
19851995
[`--diagnostic-dir`]: #--diagnostic-dirdirectory

doc/api/crypto.md

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ calling `require('crypto')` will result in an error being thrown.
4141

4242
When using CommonJS, the error thrown can be caught using try/catch:
4343

44+
<!-- eslint-skip -->
45+
4446
```cjs
4547
let crypto;
4648
try {

doc/api/globals.md

+52
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,43 @@ added: v0.1.100
281281

282282
Used to print to stdout and stderr. See the [`console`][] section.
283283

284+
## `Crypto`
285+
286+
<!-- YAML
287+
added: REPLACEME
288+
-->
289+
290+
> Stability: 1 - Experimental. Enable this API with the
291+
> [`--experimental-global-webcrypto`][] CLI flag.
292+
293+
A browser-compatible implementation of {Crypto}. This global is available
294+
only if the Node.js binary was compiled with including support for the
295+
`crypto` module.
296+
297+
## `crypto`
298+
299+
<!-- YAML
300+
added: REPLACEME
301+
-->
302+
303+
> Stability: 1 - Experimental. Enable this API with the
304+
> [`--experimental-global-webcrypto`][] CLI flag.
305+
306+
A browser-compatible implementation of the [Web Crypto API][].
307+
308+
## `CryptoKey`
309+
310+
<!-- YAML
311+
added: REPLACEME
312+
-->
313+
314+
> Stability: 1 - Experimental. Enable this API with the
315+
> [`--experimental-global-webcrypto`][] CLI flag.
316+
317+
A browser-compatible implementation of {CryptoKey}. This global is available
318+
only if the Node.js binary was compiled with including support for the
319+
`crypto` module.
320+
284321
## `Event`
285322

286323
<!-- YAML
@@ -508,6 +545,19 @@ added: v17.0.0
508545

509546
The WHATWG [`structuredClone`][] method.
510547

548+
## `SubtleCrypto`
549+
550+
<!-- YAML
551+
added: REPLACEME
552+
-->
553+
554+
> Stability: 1 - Experimental. Enable this API with the
555+
> [`--experimental-global-webcrypto`][] CLI flag.
556+
557+
A browser-compatible implementation of {SubtleCrypto}. This global is available
558+
only if the Node.js binary was compiled with including support for the
559+
`crypto` module.
560+
511561
## `DOMException`
512562

513563
<!-- YAML
@@ -572,7 +622,9 @@ The object that acts as the namespace for all W3C
572622
[WebAssembly][webassembly-org] related functionality. See the
573623
[Mozilla Developer Network][webassembly-mdn] for usage and compatibility.
574624

625+
[Web Crypto API]: webcrypto.md
575626
[`--experimental-fetch`]: cli.md#--experimental-fetch
627+
[`--experimental-global-webcrypto`]: cli.md#--experimental-global-webcrypto
576628
[`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
577629
[`DOMException`]: https://developer.mozilla.org/en-US/docs/Web/API/DOMException
578630
[`EventTarget` and `Event` API]: events.md#eventtarget-and-event-api

doc/node.1

+3
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ Enable Source Map V3 support for stack traces.
142142
.It Fl -experimental-fetch
143143
Enable experimental support for the Fetch API.
144144
.
145+
.It Fl -experimental-global-webcrypto
146+
Expose the Web Crypto API on the global scope.
147+
.
145148
.It Fl -experimental-import-meta-resolve
146149
Enable experimental ES modules support for import.meta.resolve().
147150
.

lib/.eslintrc.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ rules:
7575
message: "Use `const { atob } = require('buffer');` instead of the global."
7676
- name: btoa
7777
message: "Use `const { btoa } = require('buffer');` instead of the global."
78+
- name: crypto
79+
message: "Use `const { crypto } = require('internal/crypto/webcrypto');` instead of the global."
80+
- name: Crypto
81+
message: "Use `const { Crypto } = require('internal/crypto/webcrypto');` instead of the global."
82+
- name: CryptoKey
83+
message: "Use `const { CryptoKey } = require('internal/crypto/webcrypto');` instead of the global."
7884
- name: global
7985
message: "Use `const { globalThis } = primordials;` instead of `global`."
8086
- name: globalThis
@@ -85,6 +91,8 @@ rules:
8591
message: "Use `const { queueMicrotask } = require('internal/process/task_queues');` instead of the global."
8692
- name: structuredClone
8793
message: "Use `const { structuredClone } = require('internal/structured_clone');` instead of the global."
94+
- name: SubtleCrypto
95+
message: "Use `const { SubtleCrypto } = require('internal/crypto/webcrypto');` instead of the global."
8896
# Custom rules in tools/eslint-rules
8997
node-core/lowercase-name-for-primitive: error
9098
node-core/non-ascii-character: error

lib/internal/bootstrap/pre_execution.js

+41
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const {
44
NumberParseInt,
55
ObjectDefineProperty,
6+
ObjectGetOwnPropertyDescriptor,
67
SafeMap,
78
SafeWeakMap,
89
StringPrototypeStartsWith,
@@ -34,6 +35,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
3435
setupInspectorHooks();
3536
setupWarningHandler();
3637
setupFetch();
38+
setupWebCrypto();
3739

3840
// Resolve the coverage directory to an absolute path, and
3941
// overwrite process.env so that the original path gets passed
@@ -180,6 +182,44 @@ function setupFetch() {
180182
});
181183
}
182184

185+
// TODO(aduh95): move this to internal/bootstrap/browser when the CLI flag is
186+
// removed.
187+
function setupWebCrypto() {
188+
if (!getOptionValue('--experimental-global-webcrypto')) {
189+
return;
190+
}
191+
192+
let webcrypto;
193+
ObjectDefineProperty(globalThis, 'crypto',
194+
ObjectGetOwnPropertyDescriptor({
195+
get crypto() {
196+
webcrypto ??= require('internal/crypto/webcrypto');
197+
return webcrypto.crypto;
198+
}
199+
}, 'crypto'));
200+
if (internalBinding('config').hasOpenSSL) {
201+
webcrypto ??= require('internal/crypto/webcrypto');
202+
ObjectDefineProperty(globalThis, 'Crypto', {
203+
writable: true,
204+
enumerable: false,
205+
configurable: true,
206+
value: webcrypto.Crypto
207+
});
208+
ObjectDefineProperty(globalThis, 'CryptoKey', {
209+
writable: true,
210+
enumerable: false,
211+
configurable: true,
212+
value: webcrypto.CryptoKey
213+
});
214+
ObjectDefineProperty(globalThis, 'SubtleCrypto', {
215+
writable: true,
216+
enumerable: false,
217+
configurable: true,
218+
value: webcrypto.SubtleCrypto
219+
});
220+
}
221+
}
222+
183223
// Setup User-facing NODE_V8_COVERAGE environment variable that writes
184224
// ScriptCoverage to a specified file.
185225
function setupCoverageHooks(dir) {
@@ -521,6 +561,7 @@ module.exports = {
521561
setupCoverageHooks,
522562
setupWarningHandler,
523563
setupFetch,
564+
setupWebCrypto,
524565
setupDebugEnv,
525566
setupPerfHooks,
526567
prepareMainThreadExecution,

lib/internal/crypto/webcrypto.js

+1
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ ObjectDefineProperties(
808808

809809
module.exports = {
810810
Crypto,
811+
CryptoKey,
811812
SubtleCrypto,
812813
crypto,
813814
};

lib/internal/main/worker_thread.js

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const {
1818
setupInspectorHooks,
1919
setupWarningHandler,
2020
setupFetch,
21+
setupWebCrypto,
2122
setupDebugEnv,
2223
setupPerfHooks,
2324
initializeDeprecations,
@@ -69,6 +70,7 @@ setupDebugEnv();
6970

7071
setupWarningHandler();
7172
setupFetch();
73+
setupWebCrypto();
7274
initializeSourceMapsHandlers();
7375

7476
// Since worker threads cannot switch cwd, we do not need to

src/node_options.cc

+4
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
319319
"experimental Fetch API",
320320
&EnvironmentOptions::experimental_fetch,
321321
kAllowedInEnvironment);
322+
AddOption("--experimental-global-webcrypto",
323+
"expose experimental Web Crypto API on the global scope",
324+
&EnvironmentOptions::experimental_global_web_crypto,
325+
kAllowedInEnvironment);
322326
AddOption("--experimental-json-modules", "", NoOp{}, kAllowedInEnvironment);
323327
AddOption("--experimental-loader",
324328
"use the specified module as a custom loader",

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ class EnvironmentOptions : public Options {
108108
std::string dns_result_order;
109109
bool enable_source_maps = false;
110110
bool experimental_fetch = false;
111+
bool experimental_global_web_crypto = false;
111112
bool experimental_https_modules = false;
112113
std::string experimental_specifier_resolution;
113114
bool experimental_wasm_modules = false;

test/common/index.js

+6
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,12 @@ if (global.fetch) {
308308
global.Headers,
309309
);
310310
}
311+
if (hasCrypto && global.crypto) {
312+
knownGlobals.push(global.crypto);
313+
knownGlobals.push(global.Crypto);
314+
knownGlobals.push(global.CryptoKey);
315+
knownGlobals.push(global.SubtleCrypto);
316+
}
311317

312318
function allowGlobals(...allowlist) {
313319
knownGlobals = knownGlobals.concat(allowlist);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Flags: --experimental-global-webcrypto --expose-internals
2+
'use strict';
3+
4+
const common = require('../common');
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
8+
const assert = require('assert');
9+
const webcrypto = require('internal/crypto/webcrypto');
10+
11+
assert.strictEqual(Crypto, webcrypto.Crypto);
12+
assert.strictEqual(CryptoKey, webcrypto.CryptoKey);
13+
assert.strictEqual(SubtleCrypto, webcrypto.SubtleCrypto);
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Flags: --experimental-global-webcrypto
2+
'use strict';
3+
4+
const common = require('../common');
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
8+
const assert = require('assert');
9+
const crypto = require('crypto');
10+
11+
assert.strictEqual(globalThis.crypto, crypto.webcrypto);
12+
assert.strictEqual(Crypto, crypto.webcrypto.constructor);
13+
assert.strictEqual(SubtleCrypto, crypto.webcrypto.subtle.constructor);

0 commit comments

Comments
 (0)