EVP_BytesToKey
Reimplements OpenSSL's legacy EVP_BytesToKey key-derivation algorithm in pure JavaScript for compatibility with older OpenSSL-encrypted data.
Repository Health
Technical Analysis
evp_bytestokey is a small, dependency-light JavaScript port of OpenSSL’s EVP_BytesToKey key-derivation routine — the legacy algorithm OpenSSL used to turn a password and optional salt into a cipher key and initialization vector before PBKDF2 and scrypt became the norm. It exists purely for interoperability: encrypting or decrypting data that was originally produced with OpenSSL’s enc command or older tooling that still relies on this scheme.
The library underpins crypto-browserify, the shim that lets Node’s crypto module run in the browser, where several ciphers still expect EVP_BytesToKey-derived keys. It repeatedly hashes the password (and salt, if present) with MD5 until it has produced enough bytes to fill the requested key and IV lengths, mirroring OpenSSL’s C implementation byte-for-byte. The README is explicit that the algorithm itself is insecure by modern standards — this package is a compatibility bridge, not a recommendation.
What You Get
- A single exported function,
EVP_BytesToKey(password, salt, keyBits, ivLen), returning{ key, iv }Buffers. - Byte-for-byte parity with OpenSSL’s native EVP_BytesToKey implementation, verified in tests against a compiled C++ Node addon.
- Automatic coercion of string passwords and salts into Buffers via
safe-buffer, so callers don’t have to pre-encode input. - Salt-length validation that throws a
RangeErrorimmediately if an 8-byte salt isn’t supplied, instead of silently deriving the wrong key.
Common Use Cases
- Decrypting files or streams that were encrypted with OpenSSL’s
enc -aes-256-cbccommand line tool, which uses EVP_BytesToKey by default. - Powering crypto-browserify’s browser-side implementations of ciphers that historically relied on this key-derivation scheme.
- Interoperating with legacy systems or archived data formats that pre-date PBKDF2/scrypt-based key derivation.
- Reproducing OpenSSL’s exact key/IV output in a Node.js or browser environment for cross-platform cryptographic compatibility.
Under The Hood
Architecture
The entire package is a single 40-line CommonJS module (index.js) exporting one function — there are no internal layers, modules, or dependency-injection points to speak of, and none are needed: the whole surface area is the algorithm itself. Data flows in one direction — password and optional salt Buffers go into an iterative MD5 loop that grows a running digest (tmp) until enough bytes exist to fill pre-allocated key and iv Buffers via copy() — and out come the derived bytes, with the intermediate digest zeroed afterward. Because the export is the only public contract, any change to the hashing loop changes correctness for every downstream consumer, most notably crypto-browserify’s cipher shims.
Tech Stack
Runtime is plain Node.js/CommonJS JavaScript with two runtime dependencies: md5.js (a pure-JS MD5 implementation, used instead of Node’s native crypto module so the code also runs unmodified in the browser via crypto-browserify) and safe-buffer (a Buffer-construction shim for compatibility across older and newer Node Buffer APIs). There is no build step for the shipped code — index.js ships as-is. Dev tooling includes standard for linting, tape for tests, nyc for coverage, and a node-gyp/nan/bindings toolchain used solely to compile a native addon for test verification, with Travis CI wired up in .travis.yml.
Code Quality
The single test file drives 1,000 randomized password/salt pairs through both the JS implementation and a native C++ addon that calls OpenSSL’s real EVP_BytesToKey via nan, asserting exact equality — an unusually rigorous cross-verification strategy for a package this small, though it depends on a node-gyp rebuild step (npm run test:prepare) that grows more fragile on modern Node/OpenSSL toolchains. Error handling is explicit where it matters: a malformed salt throws a RangeError rather than silently deriving the wrong key. There is no TypeScript or bundled type definitions, and naming intentionally mirrors OpenSSL’s own C parameter names (keyBits, ivLen) for readability by anyone porting from the original algorithm rather than following idiomatic JS conventions.
API Design
The public API is one function taking four positional arguments that mirror OpenSSL’s own C signature, which minimizes remapping effort for anyone already familiar with EVP_BytesToKey but means only the salt argument is validated — a caller confusing keyBits (bits) with a byte count would get a silently wrong-length key with no runtime warning. The call is synchronous and side-effect-free, returning a plain { key, iv } object with no configuration surface, and documentation is a single README section with one runnable example rather than a dedicated examples directory or API reference.