Open-Source Wikis

/

Node.js

/

Systems

/

Crypto and TLS

nodejs/node

Crypto and TLS

Owners: @nodejs/crypto. The full module surface is documented at doc/api/crypto.md and doc/api/webcrypto.md.

Purpose

Implement the entire node:crypto surface — hashes, HMAC, ciphers, key generation, key derivation, signing/verification, randomness, X.509 — plus the WebCrypto API (globalThis.crypto.subtle) and the TLS layer used by tls, https, http2, and quic. All of this is built on OpenSSL (or BoringSSL when configured) accessed via the project's wrapper crate ncrypto.

Directory layout

lib/
  crypto.js                          // public re-exports
  internal/crypto/
    aes.js / cfrg.js / dh.js / ec.js / rsa.js / x509.js
    cipher.js / hash.js / hmac.js / keys.js / sig.js
    keygen.js / random.js / scrypt.js / pbkdf2.js / hkdf.js
    util.js / mac.js / kem.js / pqc.js
    diffiehellman.js / dsa.js / argon2.js
    webcrypto.js / webidl.js                  // WebCrypto + WebIDL parser
src/
  crypto/                            // every crypto_* C++ file (see below)
deps/
  ncrypto/                           // Node crypto wrapper crate (header-only-ish)
  openssl/                           // vendored OpenSSL or BoringSSL

src/crypto/ is a long list of focused files: crypto_aes.cc, crypto_argon2.cc, crypto_chacha20_poly1305.cc, crypto_cipher.cc, crypto_context.cc (84K — TLS context), crypto_dh.cc, crypto_dsa.cc, crypto_ec.cc, crypto_hash.cc, crypto_hkdf.cc, crypto_hmac.cc, crypto_kem.cc, crypto_keygen.cc, crypto_keys.cc (63K — key import/export), crypto_kmac.cc, crypto_pbkdf2.cc, crypto_pqc.cc, crypto_random.cc, crypto_rsa.cc, crypto_scrypt.cc, crypto_sig.cc, crypto_spkac.cc, crypto_timing.cc, crypto_tls.cc (74K — TLSWrap), crypto_turboshake.cc, crypto_util.cc, crypto_x509.cc, crypto_clienthello.cc, crypto_bio.cc.

Key abstractions

Type / file Role
KeyObject (lib/internal/crypto/keys.js) High-level key wrapper; backs subtle Crypto.
KeyObjectHandle (src/crypto/crypto_keys.cc) C++ side of KeyObject; holds an EVP_PKEY via ncrypto.
Hash / Hmac (crypto_hash.cc, crypto_hmac.cc) Streaming hash/HMAC; user JS uses Transform over these.
Cipheriv / Decipheriv (crypto_cipher.cc) AEAD + non-AEAD ciphers; libuv worker for chunked data.
RandomBytesJob (crypto_random.cc) ThreadPoolWork jobs for randomBytes/randomFill.
SecureContext (crypto_context.cc) Configures TLS (certs, ciphers, ALPN, OCSP, SNI). Used by tls and http2.
TLSWrap (crypto_tls.cc) The plaintext↔ciphertext stream; see Net/TCP/TLS/DNS.
X509Certificate (crypto_x509.cc) crypto.X509Certificate JS class.
webcrypto.js Spec-compliant WebCrypto over the same backends.
KemJob / PqcJob (crypto_kem.cc, crypto_pqc.cc) KEM and post-quantum (ML-KEM/ML-DSA) primitives.

How a streaming cipher works

sequenceDiagram
    participant JS as crypto.createCipheriv
    participant Cipher as Cipheriv (Transform)
    participant CW as CipherJob
    participant SSL as OpenSSL via ncrypto

    JS->>Cipher: new Cipheriv(alg, key, iv)
    Cipher->>CW: init EVP_CIPHER_CTX
    Cipher->>Cipher: write(buf) -> _transform
    Cipher->>SSL: EVP_EncryptUpdate
    SSL-->>Cipher: ciphertext
    Cipher-->>JS: emit data
    JS->>Cipher: end()
    Cipher->>SSL: EVP_EncryptFinal
    SSL-->>Cipher: tag (if AEAD)
    Cipher-->>JS: emit data + 'end'

For AEAD ciphers (GCM, ChaCha20-Poly1305) the auth tag is exposed via cipher.getAuthTag() after final. The cipher object also enforces setAAD ordering rules.

Async work

Hash, HMAC, KDF, and key generation all support both sync (blocks the loop) and async (libuv threadpool) APIs. The async path uses ThreadPoolWork from src/threadpoolwork-inl.h, which posts a uv_work_t and resolves a JS Promise on completion. Examples: crypto_pbkdf2.cc, crypto_scrypt.cc, crypto_random.cc, crypto_keygen.cc.

TLS (recap)

Detailed TLS layering is in Net/TCP/TLS/DNS. Highlights from the crypto side:

  • SecureContext is created by tls.createSecureContext(opts). It holds an SSL_CTX* plus auxiliary state (ALPN, sigalgs, OCSP, session ticket keys).
  • TLSWrap operates as both a StreamBase (over a parent StreamBase) and an AsyncWrap so async hooks can see TLS as an async resource.
  • crypto_clienthello.cc is the helper for inspecting a TLS ClientHello mid-handshake (used by SNI dispatch).

ncrypto wrapper

deps/ncrypto/ provides a thin C++ wrapper over OpenSSL's C API, hiding EVP_* boilerplate and reducing the divergence between OpenSSL versions. The crypto codebase is being migrated to use only ncrypto::* types instead of raw OpenSSL types — the README at src/crypto/README.md is the canonical guide.

OpenSSL / BoringSSL

Default builds use the vendored OpenSSL in deps/openssl/. The build can also link against system OpenSSL (--shared-openssl) or BoringSSL (--openssl-is-fips=BoringSSL style). The build flag and fingerprint surface as process.versions.openssl and features.openssl_is_boringssl.

WebCrypto

globalThis.crypto.subtle (and crypto.webcrypto) is implemented in lib/internal/crypto/webcrypto.js with helpers in lib/internal/crypto/keys.js, cfrg.js, aes.js, rsa.js, ec.js, etc. Each algorithm goes through a one-line "select algorithm" dispatch and into the matching helper.

The WebIDL conversion logic is in lib/internal/webidl.js (Node-internal). It is also used by URL, MessagePort, Blob, and other web-spec'd APIs.

Permission model

The crypto subsystem itself does not have a permission domain, but loaded keys can come from the FS (fs.readFile), so the FS permission domain is the gate.

Entry points for modification

  • New algorithm? Add a crypto_<alg>.{cc,h} (or extend crypto_cipher.cc for a cipher), a JS shim under lib/internal/crypto/, and document at doc/api/crypto.md and doc/api/webcrypto.md.
  • OpenSSL version bump? Driven by tools/dep_updaters/update-openssl.sh and the update-openssl.yml workflow. The Security WG owns this.
  • WebCrypto bug? lib/internal/crypto/webcrypto.js is the dispatcher; algorithm-specific modules carry the actual logic.
  • TLS regression? Start with src/crypto/crypto_tls.cc for stream behaviour and crypto_context.cc for SecureContext options.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Crypto and TLS – Node.js wiki | Factory