Skip to content

Commit

Permalink
crypto: expose Web Crypto API on the global scope
Browse files Browse the repository at this point in the history
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 <jasnell@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
  • Loading branch information
aduh95 authored Feb 14, 2022
1 parent b53f927 commit 849991c
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,5 +340,9 @@ module.exports = {
Headers: 'readable',
Request: 'readable',
Response: 'readable',
crypto: 'readable',
Crypto: 'readable',
CryptoKey: 'readable',
SubtleCrypto: 'readable',
},
};
10 changes: 10 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions doc/api/crypto.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
52 changes: 52 additions & 0 deletions doc/api/globals.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -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().
.
Expand Down
8 changes: 8 additions & 0 deletions lib/.eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
26 changes: 26 additions & 0 deletions lib/internal/bootstrap/pre_execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const {
NumberParseInt,
ObjectDefineProperty,
ObjectGetOwnPropertyDescriptor,
SafeMap,
SafeWeakMap,
StringPrototypeStartsWith,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -503,6 +528,7 @@ module.exports = {
setupCoverageHooks,
setupWarningHandler,
setupFetch,
setupWebCrypto,
setupDebugEnv,
setupPerfHooks,
prepareMainThreadExecution,
Expand Down
1 change: 1 addition & 0 deletions lib/internal/crypto/webcrypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,7 @@ ObjectDefineProperties(

module.exports = {
Crypto,
CryptoKey,
SubtleCrypto,
crypto,
};
2 changes: 2 additions & 0 deletions lib/internal/main/worker_thread.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const {
setupInspectorHooks,
setupWarningHandler,
setupFetch,
setupWebCrypto,
setupDebugEnv,
setupPerfHooks,
initializeDeprecations,
Expand Down Expand Up @@ -69,6 +70,7 @@ setupDebugEnv();

setupWarningHandler();
setupFetch();
setupWebCrypto();
initializeSourceMapsHandlers();

// Since worker threads cannot switch cwd, we do not need to
Expand Down
4 changes: 4 additions & 0 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions test/common/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 13 additions & 0 deletions test/parallel/test-global-webcrypto-classes.js
Original file line number Diff line number Diff line change
@@ -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);
13 changes: 13 additions & 0 deletions test/parallel/test-global-webcrypto.js
Original file line number Diff line number Diff line change
@@ -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);

0 comments on commit 849991c

Please sign in to comment.