mrkeyoor.com_
Wed 23 Sept 00:35 UTC
npmSecurityupdated 22 Sept 2026

ethereum-cryptography review

ethereum-cryptography 3.2.0 packages the byte-level pieces Ethereum software repeatedly needs: Keccak-256, secp256k1, BLS12-381, BN254, BIP32 and BIP39, secure random bytes, KDFs, AES, and encoding helpers. The v3 line added BLS, BN, and math modules, moved AES to synchronous non-native code, and changed the re-exported secp256k1 API. It does not know about RPC, ABI encoding, EIP-712, transactions, wallets, or custody. Our Node 22 check also found that loading the package root failed through both `require()` and ESM `import`; the documented interface is the named subpaths such as `ethereum-cryptography/keccak.js`.

Verdict

ethereum-cryptography 3.2.0 installed in 3.4 seconds with 0 audit findings, but its root CommonJS and ESM loads both failed on Node 22.23.2; install it only if explicit cryptographic subpaths fit your design. Application teams that need Ethereum conventions should start with viem or ethers, while protocol authors still need an independent review of every byte and domain rule.

We installed it

Lab card: what happened when we installed ethereum-cryptographyScreenshot of ethereum-cryptography documentation
Install✓ · 3.4s8 packages on disk · 7 MB
ImportESM import fails · require() fails · CommonJS package with exports map
Browser0.1 KBgzipped (0.1 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does ethereum-cryptography install cleanly?

Yes. In a fresh container with an empty cache, npm install ethereum-cryptography finished in 3 seconds, leaving 8 packages and 7 MB on disk. npm audit reported no known vulnerabilities.

How much does ethereum-cryptography add to a browser bundle?

0.1 KB gzipped (0.1 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does ethereum-cryptography work with both ESM and CommonJS?

Neither plain import nor require succeeded in our sandbox, so it needs a bundler or extra setup.

Does ethereum-cryptography include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

ethereum-cryptography or viem: which should you use?

viem: Use it when the job is typed Ethereum RPC, accounts, ABI work, messages, and transactions rather than raw curve and hash operations. ethereum-cryptography 3.2.0 installed in 3.4 seconds with 0 audit findings, but its root CommonJS and ESM loads both failed on Node 22.23.2; install it only if explicit cryptographic subpaths fit your design.

When should you not use ethereum-cryptography?

You need providers, contracts, ABI codecs, EIP-712, transaction signing, checksum addresses, or wallet connections. The README exposes primitives only; viem or ethers covers that protocol layer.

API stability4/5Version 3.2.0 keeps one documented subpath per primitive and pins the noble and scure packages underneath it. The upgrade section is candid about major breaks: v3 added BLS, BN, and math exports, changed AES execution, and inherited a different secp256k1 API; v2 had already changed signature return and recovery handling. Those changes stayed on major boundaries. The root-load failures we measured reduce confidence in treating the package manifest as the interface, so callers should depend on tested subpaths.
Docs5/5The README names every import path and gives runnable examples for hashes, KDFs, curves, random bytes, HD keys, mnemonics, AES, and byte conversion. Its AES section discusses password derivation, tested modes, padding, unique IVs, and error leakage instead of presenting one unsafe copy-paste block. Browser bundler settings and migrations from v1 through v3 are documented too. It deliberately does not explain transaction encoding, signed-message prefixes, or custody, so Ethereum applications still need the relevant EIPs and higher-level library docs.
Maintenance4/5GitHub showed 755 stars, 0 open issues and pull requests, an unarchived repository, and a push on May 27, 2026. npm still lists 3.2.0, published April 25, 2025, while the latest repository commit is a merged v4 draft using Noble 2.2. That is active work, though it also means main is ahead of the stable package. The dependency versions are deliberately pinned and the README links a January 2022 Cure53 audit; consumers should distinguish that older audit from review of the pending v4 code.
Ecosystem5/5The npm downloads endpoint counted 4,346,582 downloads for August 18 through August 24, 2026. Its primitives match Ethereum's common hashes, secp256k1 signatures, BLS and BN curves, BIP32 derivation, and BIP39 mnemonics, while the underlying noble and scure projects are available separately. That reach makes compatibility easier, but the package intentionally stops before providers, ABI codecs, wallet connectors, transaction builders, and secure storage. Viem and ethers remain the surrounding application ecosystems.

Use it if

  • You are writing Ethereum infrastructure and need Keccak, secp256k1, BLS, BN254, or wallet derivation as byte-oriented primitives.
  • You want the noble and scure implementations behind one package whose dependency versions are pinned rather than floated.
  • Your browser and Node builds can import only the explicit subpaths they use and can pass `Uint8Array` values across the boundary.
  • Your team already has a reviewed protocol layer for message framing, key custody, authenticated encryption, and error handling.
Skip it if

Setup reality

We installed ethereum-cryptography 3.2.0 in 3.4 seconds in a fresh Node 22 Bookworm sandbox. The install succeeded and left 8 packages using 7 MB. npm audit reported 0 known vulnerabilities. The package itself is 508 KB unpacked, declares 5 direct dependencies and no peers, bundles TypeScript declarations, and requires Node ^14.21.3 || >=16 with npm 9 or newer.

The surprise came after installation. The package is marked CommonJS and has an exports map, yet both root require() and root ESM import failed on Node 22.23.2. Use the README's explicit paths, for example ethereum-cryptography/keccak.js or /secp256k1.js, and test those paths in your actual loader. Our all-package esbuild probe produced only a 0.1 KB minified, 0.1 KB gzipped stub, which is another reason not to read that number as the cost of the useful submodules.

No credentials, native compiler, or config file is involved. Inputs are normally Uint8Array; convert text with utf8ToBytes and display output with bytesToHex. BIP39 wordlists live behind separate imports. Random bytes come from crypto.getRandomValues in browsers or crypto.randomBytes in Node and the call throws when neither secure source exists.

The package leaves Ethereum framing to you. secp256k1.sign signs a digest, Keccak-256 is different from standardized SHA3-256, and a lowercase 20-byte address is not an EIP-55 checksum address. PBKDF2 and scrypt parameters, salts, worker limits, AES IV storage, authentication, and uniform decrypt errors remain application decisions. The README links a Cure53 audit dated January 5, 2022; it does not cover your surrounding protocol.

Patterns

Hash UTF-8 data with Ethereum Keccak-256 hash-keccak256

import { keccak256 } from 'ethereum-cryptography/keccak.js';
import { bytesToHex, utf8ToBytes } from 'ethereum-cryptography/utils.js';

const digest = keccak256(utf8ToBytes('hello'));
console.log(bytesToHex(digest));

Ethereum uses Keccak-256 here, not standardized SHA3-256. The two functions return different digests for the same input.

Compute a SHA-256 digest hash-sha256

import { sha256 } from 'ethereum-cryptography/sha256.js';
import { bytesToHex, utf8ToBytes } from 'ethereum-cryptography/utils.js';

const digestHex = bytesToHex(sha256(utf8ToBytes('artifact contents')));

Decode hexadecimal input before hashing it. Hashing the UTF-8 characters `0a` is different from hashing the single byte `0x0a`.

Generate a valid secp256k1 private key generate-private-key

import { secp256k1 } from 'ethereum-cryptography/secp256k1.js';
import { bytesToHex } from 'ethereum-cryptography/utils.js';

const privateKey = secp256k1.utils.randomPrivateKey();
const publicKey = secp256k1.getPublicKey(privateKey, false);
console.log(bytesToHex(publicKey));

The random private key is secret application data. The helper uses the package's secure randomness source, but logging the result still compromises it.

Derive a lowercase Ethereum address derive-ethereum-address

import { secp256k1 } from 'ethereum-cryptography/secp256k1.js';
import { keccak256 } from 'ethereum-cryptography/keccak.js';
import { bytesToHex } from 'ethereum-cryptography/utils.js';

const publicKey = secp256k1.getPublicKey(privateKey, false);
const addressBytes = keccak256(publicKey.slice(1)).slice(-20);
const address = `0x${bytesToHex(addressBytes)}`;

This produces a lowercase 20-byte address. EIP-55 checksum casing requires another step, and the uncompressed public-key prefix must be removed before hashing.

Sign and verify a 32-byte digest sign-and-verify-digest

import { secp256k1 } from 'ethereum-cryptography/secp256k1.js';
import { keccak256 } from 'ethereum-cryptography/keccak.js';
import { utf8ToBytes } from 'ethereum-cryptography/utils.js';

const digest = keccak256(utf8ToBytes('domain-separated message'));
const signature = secp256k1.sign(digest, privateKey);
const publicKey = secp256k1.getPublicKey(privateKey);
const valid = secp256k1.verify(signature, digest, publicKey);

The function signs the 32-byte digest exactly as supplied. It adds neither the EIP-191 personal-message prefix nor EIP-712 domain data.

Recover the public key from a signature recover-public-key

const signature = secp256k1.sign(digest, privateKey);
const recoveredPoint = signature.recoverPublicKey(digest);
const recoveredPublicKey = recoveredPoint.toRawBytes(false);
const expectedPublicKey = secp256k1.getPublicKey(privateKey, false);

In the v2 and v3 API, recovery lives on the signature object. Preserve its recovery bit if a later process must recover the public key.

Generate and validate an English BIP39 mnemonic generate-mnemonic

import { generateMnemonic, validateMnemonic } from 'ethereum-cryptography/bip39/index.js';
import { wordlist } from 'ethereum-cryptography/bip39/wordlists/english.js';

const mnemonic = generateMnemonic(wordlist);
if (!validateMnemonic(mnemonic, wordlist)) throw new Error('invalid mnemonic');

The English wordlist is a separate import. A mnemonic exposes every derived account, so it must never enter logs, analytics, or crash reports.

Derive an Ethereum BIP44 child key derive-hd-wallet

import { mnemonicToSeed } from 'ethereum-cryptography/bip39/index.js';
import { wordlist } from 'ethereum-cryptography/bip39/wordlists/english.js';
import { validateMnemonic } from 'ethereum-cryptography/bip39/index.js';
import { HDKey } from 'ethereum-cryptography/hdkey.js';

if (!validateMnemonic(mnemonic, wordlist)) throw new Error('invalid mnemonic');
const seed = await mnemonicToSeed(mnemonic, passphrase);
const account = HDKey.fromMasterSeed(seed).derive("m/44'/60'/0'/0/0");
if (!account.privateKey) throw new Error('private key unavailable');

The passphrase is part of BIP39 seed derivation. A different or lost passphrase deterministically opens a different wallet tree.

Derive key bytes with asynchronous PBKDF2 derive-key-pbkdf2

import { pbkdf2 } from 'ethereum-cryptography/pbkdf2.js';
import { getRandomBytes } from 'ethereum-cryptography/random.js';
import { utf8ToBytes } from 'ethereum-cryptography/utils.js';

const salt = await getRandomBytes(16);
const key = await pbkdf2(utf8ToBytes(password), salt, 131072, 32, 'sha256');

Store the random salt and parameter set beside the derived result. Tune iteration count on your supported hardware rather than copying it blindly.

Derive key bytes with asynchronous scrypt derive-key-scrypt

import { scrypt } from 'ethereum-cryptography/scrypt.js';
import { getRandomBytes } from 'ethereum-cryptography/random.js';
import { utf8ToBytes } from 'ethereum-cryptography/utils.js';

const salt = await getRandomBytes(16);
const key = await scrypt(utf8ToBytes(password), salt, 262144, 8, 1, 32);

scrypt can consume substantial CPU and memory. Cap parallel calls and never accept N, r, or p directly from an unauthenticated request.

Decrypt an existing AES-CBC payload decrypt-legacy-aes

import { decrypt } from 'ethereum-cryptography/aes.js';

function decryptLegacy(ciphertext, key, iv) {
  try {
    return decrypt(ciphertext, key, iv, 'aes-256-cbc', true);
  } catch {
    throw new Error('decryption failed');
  }
}

CBC does not authenticate ciphertext, and the README limits this mode to existing data. Collapse all decrypt failures into the same application error to avoid exposing padding details.

Sign and verify with BLS12-381 sign-bls-message

import { bls12_381 as bls } from 'ethereum-cryptography/bls.js';

const privateKey = bls.utils.randomPrivateKey();
const publicKey = bls.getPublicKey(privateKey);
const signature = bls.sign(messageBytes, privateKey);
const valid = bls.verify(signature, messageBytes, publicKey);

A BLS round trip does not select a protocol ciphersuite. Follow the protocol's domain tag, key validation, aggregation, and proof-of-possession rules.

Alternatives

PackageRegistryPick it when
viemnpmUse it when the job is typed Ethereum RPC, accounts, ABI work, messages, and transactions rather than raw curve and hash operations.
ethersnpmUse it for providers, contracts, wallets, signing conventions, and the broad utilities expected by an Ethereum application.
@noble/hashesnpmInstall it directly when hashes and KDFs are the complete requirement and you can track noble releases yourself.
@noble/curvesnpmInstall it directly for current curve implementations without the Ethereum compatibility wrapper and wallet helpers.

More security guides

cryptography · pyjwt · jose · requests-oauthlib · oauthlib · dompurify · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.