denoland/deno · error · Error
Private key is not valid for specified curve
Error message
Private key is not valid for specified curve
What it means
setPrivateKey() validates the supplied bytes with op_node_ecdh_validate_private_key and throws this Error when they are not a legal private scalar for the curve: wrong byte length, an all-zero value, or a value at or above the curve order. The polyfill matches Node's behavior of rejecting the import rather than storing a unusable key.
Source
Thrown at ext/node/polyfills/internal/crypto/diffiehellman.ts:1557
pubbuf[0] = compressedBuf[0] + 4;
}
return ecdhEncode(pubbuf, encoding ?? "buffer");
}
setPrivateKey(
privateKey: ArrayBufferView | string,
encoding?: any,
): Buffer | string {
let privbuf: Buffer;
if (typeof privateKey === "string") {
privbuf = Buffer.from(privateKey, encoding);
} else {
const parts = getViewParts(privateKey);
privbuf = Buffer.from(parts.ab, parts.off, parts.len);
}
if (!op_node_ecdh_validate_private_key(this.#curve.name, privbuf)) {
throw new Error("Private key is not valid for specified curve");
}
const pubbuf = Buffer.alloc(this.#curve.publicKeySize);
op_node_ecdh_compute_public_key(this.#curve.name, privbuf, pubbuf);
this.#privbuf = privbuf;
this.#pubbuf = pubbuf;
return pubbuf;
}
setPublicKey(
publicKey: ArrayBufferView | string,
encoding?: any,
): void {
let pubbuf: Buffer;
if (typeof publicKey === "string") {
pubbuf = Buffer.from(publicKey, encoding);View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Match the byte length to the curve: P-256/X25519/secp256k1: 32; P-384: 48; P-521: 66
- Decode hex/base64 strings with the explicit encoding: Buffer.from(privHex, 'hex')
- Left-pad or right-trim with 0x00 to the exact field size if the key came from a system that drops leading zeros
- Confirm you are importing the private scalar, not the public key or a DER wrapper (unwrap DER first)
Example fix
// before ecdh.setPrivateKey(Buffer.from(privHex)); // utf8 bytes of hex text -> wrong length // after ecdh.setPrivateKey(Buffer.from(privHex, 'hex')); // 32 bytes for P-256
Defensive patterns
Strategy: validation
Validate before calling
const PRIV_LEN = { prime256v1: 32, secp384r1: 48, secp521r1: 66, X25519: 32 }[curve]!;
if (privBuf.length !== PRIV_LEN || privBuf.every((b) => b === 0)) {
throw new RangeError(`private key must be ${PRIV_LEN} non-zero bytes for ${curve}`);
}
ecdh.setPrivateKey(privBuf); Type guard
const isCurvePrivateScalar = (b: Uint8Array, curve: string): boolean => {
const sizes: Record<string, number> = { prime256v1: 32, secp384r1: 48, secp521r1: 66, X25519: 32 };
return b.length === sizes[curve] && b.some((x) => x !== 0);
}; Try / catch
catch (e) { if (e.message === 'Private key is not valid for specified curve') { /* check encoding + length of stored key */ } throw e; } Prevention
- Always decode stored keys with an explicit encoding: Buffer.from(hex, 'hex')
- Left-pad stored scalars that had leading zero bytes stripped
- Validate key length against the curve constant in your key-import module
When it happens
Trigger: ecdh.setPrivateKey(buf) where buf has the wrong size for the curve (e.g. 30 or 64 bytes for P-256, which expects 32), is zero, or >= n; passing the public key by mistake; decoding a hex private key string without the 'hex' encoding so it becomes 64 utf8 bytes.
Common situations: Reading a stored hex key with Buffer.from(key) instead of Buffer.from(key, 'hex'); interoperating with systems that pad or strip leading zero bytes inconsistently; copying a secp256k1 key into a P-256 instance (both 32 bytes, but the value can exceed P-256's order).
Related errors
- ERR_CRYPTO_ECDH_INVALID_FORMAT
- Invalid key type
- ERR_INVALID_ARG_TYPE
- ERR_OSSL_DH_MODULUS_TOO_SMALL
- ERR_OSSL_DH_BAD_GENERATOR
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/1113fc69b7c19e72.
Report an issue: GitHub.