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 BoringSSLsrc/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:
SecureContextis created bytls.createSecureContext(opts). It holds anSSL_CTX*plus auxiliary state (ALPN, sigalgs, OCSP, session ticket keys).TLSWrapoperates as both aStreamBase(over a parentStreamBase) and anAsyncWrapso async hooks can see TLS as an async resource.crypto_clienthello.ccis 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 extendcrypto_cipher.ccfor a cipher), a JS shim underlib/internal/crypto/, and document atdoc/api/crypto.mdanddoc/api/webcrypto.md. - OpenSSL version bump? Driven by
tools/dep_updaters/update-openssl.shand theupdate-openssl.ymlworkflow. The Security WG owns this. - WebCrypto bug?
lib/internal/crypto/webcrypto.jsis the dispatcher; algorithm-specific modules carry the actual logic. - TLS regression? Start with
src/crypto/crypto_tls.ccfor stream behaviour andcrypto_context.ccfor SecureContext options.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.