Skip to content

globals: expose WebCrypto namespace and constructors #2576

@andrewtdiz

Description

@andrewtdiz

Summary

Node exposes WebCrypto both as a global browser-compatible surface and through node:crypto.webcrypto. Perry has substantial node:crypto / crypto.subtle.<method>() lowering support, but the native global object and module namespace shape appear incomplete: globalThis.crypto, Crypto, SubtleCrypto, CryptoKey, and crypto.webcrypto are not exposed as real WebCrypto objects/classes.

This breaks browser-compatible libraries that do feature detection or identity checks before calling WebCrypto, e.g. globalThis.crypto instanceof Crypto, crypto.subtle instanceof SubtleCrypto, key instanceof CryptoKey, or require("node:crypto").webcrypto === globalThis.crypto.

Expected Node behavior

Current Node docs list crypto, Crypto, CryptoKey, and SubtleCrypto as globals, and the Web Crypto docs state that globalThis.crypto is a Crypto singleton. The node:crypto docs also document crypto.webcrypto as a Crypto object.

Docs:

Local Node v25.9.0 probe:

crypto object Crypto
Crypto function Function
Crypto proto constructor,getRandomValues,randomUUID,subtle
SubtleCrypto function Function
SubtleCrypto proto constructor,decapsulateBits,decapsulateKey,decrypt,deriveBits,deriveKey,digest,encapsulateBits,encapsulateKey,encrypt,exportKey,generateKey,getPublicKey,importKey,sign,unwrapKey,verify,wrapKey
CryptoKey function Function
CryptoKey proto algorithm,constructor,extractable,type,usages
crypto instanceof Crypto true
crypto.subtle instanceof SubtleCrypto true
key instanceof CryptoKey true secret true HMAC sign,verify
module.webcrypto type object Crypto
module.webcrypto === globalThis.crypto true
module.webcrypto.subtle instanceof SubtleCrypto true

Current Perry evidence

From origin/main source inspection:

  • crates/perry-runtime/src/object/global_this.rs populates constructor globals such as TextEncoder, URL, AbortSignal, Blob, Request, and Response, but not Crypto, SubtleCrypto, or CryptoKey. It also creates a special performance global, but no crypto global.
  • crates/perry-codegen/src/expr/helpers.rs::is_global_this_builtin_name similarly has no crypto, Crypto, SubtleCrypto, or CryptoKey routing for globalThis.<name> reads.
  • crates/perry-hir/src/lower/lower_expr.rs treats bare crypto as a known global identifier to avoid warnings, but bare global values still lower through the generic GlobalGet(0) sentinel unless a parent call/member pattern recognizes them.
  • crates/perry-runtime/src/object/native_module.rs materializes crypto.subtle as a sub-namespace for import-style code, but there is no webcrypto property in the crypto module property handling.
  • crates/perry-api-manifest/src/entries.rs lists only property("crypto", "subtle") for the WebCrypto namespace; there is no property("crypto", "webcrypto"), class("crypto", "Crypto"), class("crypto", "SubtleCrypto"), or global constructor inventory.
  • docs/api/perry.d.ts declares export const subtle: any in crypto, but no webcrypto, Crypto, SubtleCrypto, or CryptoKey class declarations.
  • Existing WebCrypto key material is represented through internal Buffer-backed registry objects, so instanceof CryptoKey / prototype identity is not currently represented as a Node-compatible class surface.

I could not run a fresh Perry binary locally in this checkout, so this is based on origin/main source, current Node docs, and local Node probes.

Suggested test surface

Add parity cases that check both shape and identity before algorithm details:

import cryptoModule from "node:crypto";

console.log(typeof globalThis.crypto, globalThis.crypto.constructor.name);
console.log(typeof Crypto, typeof SubtleCrypto, typeof CryptoKey);
console.log(globalThis.crypto instanceof Crypto);
console.log(globalThis.crypto.subtle instanceof SubtleCrypto);
console.log(cryptoModule.webcrypto === globalThis.crypto);
console.log(cryptoModule.webcrypto.subtle === globalThis.crypto.subtle);

const key = await crypto.subtle.generateKey({ name: "HMAC", hash: "SHA-256" }, true, ["sign", "verify"]);
console.log(key instanceof CryptoKey, key.type, key.extractable, key.algorithm.name, key.usages.join(","));

const bytes = new Uint8Array(4);
console.log(globalThis.crypto.getRandomValues(bytes) === bytes, bytes.length);
console.log(typeof globalThis.crypto.randomUUID());

Scope / non-goals

This issue is about WebCrypto namespace/class shape, global exposure, and identity/prototype behavior. It should not duplicate algorithm-support work in #2518, KeyObject conversion/class work in #2565, or util.types.isCryptoKey predicate work in #2552. Unsupported or experimental algorithms can still throw Node-shaped errors after the objects/classes themselves exist.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions