crypto: expose Web Crypto API on the global scope

PR-URL: https://github.com/nodejs/node/pull/41938
Refs: https://developer.mozilla.org/en-US/docs/Web/API/crypto_property
Refs: https://github.com/nodejs/node/pull/41782
Refs: https://w3c.github.io/webcrypto
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
This commit is contained in:
Antoine du Hamel 2022-02-14 17:14:49 +01:00 committed by GitHub
parent b53f927a52
commit 849991c6c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 145 additions and 0 deletions

View file

@ -340,5 +340,9 @@ module.exports = {
Headers: 'readable',
Request: 'readable',
Response: 'readable',
crypto: 'readable',
Crypto: 'readable',
CryptoKey: 'readable',
SubtleCrypto: 'readable',
},
};

View file

@ -288,6 +288,14 @@ added: v17.5.0
Enable experimental support for the [Fetch API][].
### `--experimental-global-webcrypto`
<!-- YAML
added: REPLACEME
-->
Expose the [Web Crypto API][] on the global scope.
### `--experimental-import-meta-resolve`
<!-- YAML
@ -1580,6 +1588,7 @@ Node.js options that are allowed are:
* `--enable-source-maps`
* `--experimental-abortcontroller`
* `--experimental-fetch`
* `--experimental-global-webcrypto`
* `--experimental-import-meta-resolve`
* `--experimental-json-modules`
* `--experimental-loader`
@ -1982,6 +1991,7 @@ $ node --max-old-space-size=1536 index.js
[Source Map]: https://sourcemaps.info/spec.html
[Subresource Integrity]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
[V8 JavaScript code coverage]: https://v8project.blogspot.com/2017/12/javascript-code-coverage.html
[Web Crypto API]: webcrypto.md
[`"type"`]: packages.md#type
[`--cpu-prof-dir`]: #--cpu-prof-dir
[`--diagnostic-dir`]: #--diagnostic-dirdirectory

View file

@ -41,6 +41,8 @@ calling `require('crypto')` will result in an error being thrown.
When using CommonJS, the error thrown can be caught using try/catch:
<!-- eslint-skip -->
```cjs
let crypto;
try {

View file

@ -307,6 +307,43 @@ added: v0.1.100
Used to print to stdout and stderr. See the [`console`][] section.
## `Crypto`
<!-- YAML
added: REPLACEME
-->
> Stability: 1 - Experimental. Enable this API with the
> [`--experimental-global-webcrypto`][] CLI flag.
A browser-compatible implementation of {Crypto}. This global is available
only if the Node.js binary was compiled with including support for the
`crypto` module.
## `crypto`
<!-- YAML
added: REPLACEME
-->
> Stability: 1 - Experimental. Enable this API with the
> [`--experimental-global-webcrypto`][] CLI flag.
A browser-compatible implementation of the [Web Crypto API][].
## `CryptoKey`
<!-- YAML
added: REPLACEME
-->
> Stability: 1 - Experimental. Enable this API with the
> [`--experimental-global-webcrypto`][] CLI flag.
A browser-compatible implementation of {CryptoKey}. This global is available
only if the Node.js binary was compiled with including support for the
`crypto` module.
## `Event`
<!-- YAML
@ -534,6 +571,19 @@ added: v17.0.0
The WHATWG [`structuredClone`][] method.
## `SubtleCrypto`
<!-- YAML
added: REPLACEME
-->
> Stability: 1 - Experimental. Enable this API with the
> [`--experimental-global-webcrypto`][] CLI flag.
A browser-compatible implementation of {SubtleCrypto}. This global is available
only if the Node.js binary was compiled with including support for the
`crypto` module.
## `DOMException`
<!-- YAML
@ -598,7 +648,9 @@ The object that acts as the namespace for all W3C
[WebAssembly][webassembly-org] related functionality. See the
[Mozilla Developer Network][webassembly-mdn] for usage and compatibility.
[Web Crypto API]: webcrypto.md
[`--experimental-fetch`]: cli.md#--experimental-fetch
[`--experimental-global-webcrypto`]: cli.md#--experimental-global-webcrypto
[`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
[`DOMException`]: https://developer.mozilla.org/en-US/docs/Web/API/DOMException
[`EventTarget` and `Event` API]: events.md#eventtarget-and-event-api

View file

@ -142,6 +142,9 @@ Enable Source Map V3 support for stack traces.
.It Fl -experimental-fetch
Enable experimental support for the Fetch API.
.
.It Fl -experimental-global-webcrypto
Expose the Web Crypto API on the global scope.
.
.It Fl -experimental-import-meta-resolve
Enable experimental ES modules support for import.meta.resolve().
.

View file

@ -79,6 +79,12 @@ rules:
message: "Use `const { atob } = require('buffer');` instead of the global."
- name: btoa
message: "Use `const { btoa } = require('buffer');` instead of the global."
- name: crypto
message: "Use `const { crypto } = require('internal/crypto/webcrypto');` instead of the global."
- name: Crypto
message: "Use `const { Crypto } = require('internal/crypto/webcrypto');` instead of the global."
- name: CryptoKey
message: "Use `const { CryptoKey } = require('internal/crypto/webcrypto');` instead of the global."
- name: global
message: "Use `const { globalThis } = primordials;` instead of `global`."
- name: globalThis
@ -89,6 +95,8 @@ rules:
message: "Use `const { queueMicrotask } = require('internal/process/task_queues');` instead of the global."
- name: structuredClone
message: "Use `const { structuredClone } = require('internal/structured_clone');` instead of the global."
- name: SubtleCrypto
message: "Use `const { SubtleCrypto } = require('internal/crypto/webcrypto');` instead of the global."
# Custom rules in tools/eslint-rules
node-core/lowercase-name-for-primitive: error
node-core/non-ascii-character: error

View file

@ -3,6 +3,7 @@
const {
NumberParseInt,
ObjectDefineProperty,
ObjectGetOwnPropertyDescriptor,
SafeMap,
SafeWeakMap,
StringPrototypeStartsWith,
@ -36,6 +37,7 @@ function prepareMainThreadExecution(expandArgv1 = false) {
setupInspectorHooks();
setupWarningHandler();
setupFetch();
setupWebCrypto();
// Resolve the coverage directory to an absolute path, and
// overwrite process.env so that the original path gets passed
@ -162,6 +164,29 @@ function setupFetch() {
exposeInterface(globalThis, 'Response', undici.Response);
}
// TODO(aduh95): move this to internal/bootstrap/browser when the CLI flag is
// removed.
function setupWebCrypto() {
if (!getOptionValue('--experimental-global-webcrypto')) {
return;
}
let webcrypto;
ObjectDefineProperty(globalThis, 'crypto',
ObjectGetOwnPropertyDescriptor({
get crypto() {
webcrypto ??= require('internal/crypto/webcrypto');
return webcrypto.crypto;
}
}, 'crypto'));
if (internalBinding('config').hasOpenSSL) {
webcrypto ??= require('internal/crypto/webcrypto');
exposeInterface(globalThis, 'Crypto', webcrypto.Crypto);
exposeInterface(globalThis, 'CryptoKey', webcrypto.CryptoKey);
exposeInterface(globalThis, 'SubtleCrypto', webcrypto.SubtleCrypto);
}
}
// Setup User-facing NODE_V8_COVERAGE environment variable that writes
// ScriptCoverage to a specified file.
function setupCoverageHooks(dir) {
@ -503,6 +528,7 @@ module.exports = {
setupCoverageHooks,
setupWarningHandler,
setupFetch,
setupWebCrypto,
setupDebugEnv,
setupPerfHooks,
prepareMainThreadExecution,

View file

@ -808,6 +808,7 @@ ObjectDefineProperties(
module.exports = {
Crypto,
CryptoKey,
SubtleCrypto,
crypto,
};

View file

@ -18,6 +18,7 @@ const {
setupInspectorHooks,
setupWarningHandler,
setupFetch,
setupWebCrypto,
setupDebugEnv,
setupPerfHooks,
initializeDeprecations,
@ -69,6 +70,7 @@ setupDebugEnv();
setupWarningHandler();
setupFetch();
setupWebCrypto();
initializeSourceMapsHandlers();
// Since worker threads cannot switch cwd, we do not need to

View file

@ -319,6 +319,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"experimental Fetch API",
&EnvironmentOptions::experimental_fetch,
kAllowedInEnvironment);
AddOption("--experimental-global-webcrypto",
"expose experimental Web Crypto API on the global scope",
&EnvironmentOptions::experimental_global_web_crypto,
kAllowedInEnvironment);
AddOption("--experimental-json-modules", "", NoOp{}, kAllowedInEnvironment);
AddOption("--experimental-loader",
"use the specified module as a custom loader",

View file

@ -108,6 +108,7 @@ class EnvironmentOptions : public Options {
std::string dns_result_order;
bool enable_source_maps = false;
bool experimental_fetch = false;
bool experimental_global_web_crypto = false;
bool experimental_https_modules = false;
std::string experimental_specifier_resolution;
bool experimental_wasm_modules = false;

View file

@ -308,6 +308,12 @@ if (global.fetch) {
global.Headers,
);
}
if (hasCrypto && global.crypto) {
knownGlobals.push(global.crypto);
knownGlobals.push(global.Crypto);
knownGlobals.push(global.CryptoKey);
knownGlobals.push(global.SubtleCrypto);
}
function allowGlobals(...allowlist) {
knownGlobals = knownGlobals.concat(allowlist);

View file

@ -0,0 +1,13 @@
// Flags: --experimental-global-webcrypto --expose-internals
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const webcrypto = require('internal/crypto/webcrypto');
assert.strictEqual(Crypto, webcrypto.Crypto);
assert.strictEqual(CryptoKey, webcrypto.CryptoKey);
assert.strictEqual(SubtleCrypto, webcrypto.SubtleCrypto);

View file

@ -0,0 +1,13 @@
// Flags: --experimental-global-webcrypto
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
const assert = require('assert');
const crypto = require('crypto');
assert.strictEqual(globalThis.crypto, crypto.webcrypto);
assert.strictEqual(Crypto, crypto.webcrypto.constructor);
assert.strictEqual(SubtleCrypto, crypto.webcrypto.subtle.constructor);