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 and danielleadams committed Apr 24, 2022
1 parent 81e56c9 commit a592442
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .eslintrc.js
Expand Up @@ -339,5 +339,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
Expand Up @@ -280,6 +280,14 @@ added: REPLACEME

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 @@ -1549,6 +1557,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 @@ -1948,6 +1957,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
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
Expand Up @@ -261,6 +261,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 @@ -489,6 +526,19 @@ added: v0.0.1

[`setTimeout`][] is described in the [timers][] section.

## `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.

## `TextDecoder`

<!-- YAML
Expand Down Expand Up @@ -543,7 +593,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
[`EventTarget` and `Event` API]: events.md#eventtarget-and-event-api
[`MessageChannel`]: worker_threads.md#class-messagechannel
Expand Down
3 changes: 3 additions & 0 deletions doc/node.1
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
Expand Up @@ -77,6 +77,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 @@ -85,6 +91,8 @@ rules:
message: "Use `const { performance } = require('perf_hooks');` instead of the global."
- name: queueMicrotask
message: "Use `const { queueMicrotask } = require('internal/process/task_queues');` 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
41 changes: 41 additions & 0 deletions lib/internal/bootstrap/pre_execution.js
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 @@ -163,6 +165,44 @@ 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');
ObjectDefineProperty(globalThis, 'Crypto', {
writable: true,
enumerable: false,
configurable: true,
value: webcrypto.Crypto
});
ObjectDefineProperty(globalThis, 'CryptoKey', {
writable: true,
enumerable: false,
configurable: true,
value: webcrypto.CryptoKey
});
ObjectDefineProperty(globalThis, 'SubtleCrypto', {
writable: true,
enumerable: false,
configurable: true,
value: webcrypto.SubtleCrypto
});
}
}

// Setup User-facing NODE_V8_COVERAGE environment variable that writes
// ScriptCoverage to a specified file.
function setupCoverageHooks(dir) {
Expand Down Expand Up @@ -504,6 +544,7 @@ module.exports = {
setupCoverageHooks,
setupWarningHandler,
setupFetch,
setupWebCrypto,
setupDebugEnv,
setupPerfHooks,
prepareMainThreadExecution,
Expand Down
1 change: 1 addition & 0 deletions lib/internal/crypto/webcrypto.js
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
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
Expand Up @@ -317,6 +317,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
&EnvironmentOptions::experimental_fetch,
kAllowedInEnvironment);
AddOption("--experimental-json-modules", "", NoOp{}, kAllowedInEnvironment);
AddOption("--experimental-global-webcrypto",
"expose experimental Web Crypto API on the global scope",
&EnvironmentOptions::experimental_global_web_crypto,
kAllowedInEnvironment);
AddOption("--experimental-loader",
"use the specified module as a custom loader",
&EnvironmentOptions::userland_loader,
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Expand Up @@ -105,6 +105,7 @@ class EnvironmentOptions : public Options {
bool enable_source_maps = false;
bool experimental_https_modules = false;
bool experimental_fetch = false;
bool experimental_global_web_crypto = false;
std::string experimental_specifier_resolution;
bool experimental_wasm_modules = false;
bool experimental_import_meta_resolve = false;
Expand Down
7 changes: 7 additions & 0 deletions test/common/index.js
Expand Up @@ -303,6 +303,13 @@ if (global.fetch) {
);
}

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
@@ -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
@@ -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 a592442

Please sign in to comment.