{"id":10705,"library":"curve25519-js","title":"Curve25519 Signatures and Key Agreement","description":"curve25519-js provides a JavaScript implementation of Curve25519, facilitating both digital signatures and X25519 Diffie-Hellman key agreement. The current stable version is 0.0.4. While its release cadence appears infrequent, with a significant rewrite in 2019, it serves as a functional library for cryptographic operations. A key differentiator is its ability to use a single X25519 key for both signing and key agreement, a feature that distinguishes it from standard Ed25519 implementations which typically use separate key types or require explicit conversion. This is achieved by embedding and extracting a sign bit into the signature during the process. The library is derived from TweetNaCl.js and is suitable for environments where direct Curve25519 operations are needed.","status":"maintenance","version":"0.0.4","language":"javascript","source_language":"en","source_url":"https://github.com/harveyconnor/curve25519-js","tags":["javascript","sign","curve25519","x25519","ed25519"],"install":[{"cmd":"npm install curve25519-js","lang":"bash","label":"npm"},{"cmd":"yarn add curve25519-js","lang":"bash","label":"yarn"},{"cmd":"pnpm add curve25519-js","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"CommonJS require() syntax is also supported, but ESM import is preferred in modern JavaScript environments as shown in the README.","wrong":"const { sharedKey } = require('curve25519-js');","symbol":"sharedKey","correct":"import { sharedKey } from 'curve25519-js';"},{"note":"For generating new Curve25519 key pairs from a secure random seed.","wrong":"const generateKeyPair = require('curve25519-js').generateKeyPair;","symbol":"generateKeyPair","correct":"import { generateKeyPair } from 'curve25519-js';"},{"note":"All core functions are named exports from the main package entry point.","wrong":"import sign from 'curve25519-js/sign';","symbol":"sign","correct":"import { sign } from 'curve25519-js';"},{"note":"While `Buffer` is a global in Node.js, explicitly importing it or ensuring it's available (e.g., via a polyfill like `buffer` for Webpack 5+) is necessary for browser environments where it's used in examples for hex conversion.","wrong":"const Buffer = require('buffer');","symbol":"Buffer","correct":"import { Buffer } from 'buffer';"}],"quickstart":{"code":"import { sharedKey } from 'curve25519-js';\nimport { Buffer } from 'buffer'; // Explicit import for browser compatibility\n\nconst ALICE_PRIV = '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a';\nconst BOB_PUB = 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f';\n\n// Ensure Buffer is available for hex conversion\n// In a browser environment, you might need a polyfill or alternative conversion\nconst alicePriv = Uint8Array.from(Buffer.from(ALICE_PRIV, 'hex'));\nconst bobPub = Uint8Array.from(Buffer.from(BOB_PUB, 'hex'));\n\n// Perform the Diffie-Hellman key exchange to get the shared secret\nconst secret = sharedKey(alicePriv, bobPub);\n\nconsole.log('Secret:', Buffer.from(secret).toString('hex'));\n\n// Example of key generation (requires a CSPRNG seed)\nimport { generateKeyPair } from 'curve25519-js';\nimport crypto from 'crypto'; // For Node.js CSPRNG\n\nconst seed = crypto.randomBytes(32); // Generate a 32-byte cryptographically secure random seed\nconst keyPair = generateKeyPair(seed);\nconsole.log('Generated Private Key:', Buffer.from(keyPair.private).toString('hex'));\nconsole.log('Generated Public Key:', Buffer.from(keyPair.public).toString('hex'));","lang":"javascript","description":"This quickstart demonstrates how to compute a shared secret key using `sharedKey` with pre-defined private and public keys, and also shows how to generate a new key pair using `generateKeyPair` with a cryptographically secure random seed."},"warnings":[{"fix":"Apply a Key Derivation Function (KDF) like HKDF or a strong cryptographic hash function (e.g., SHA256) to the `sharedKey` output before using it as an encryption key. Example: `const derivedKey = crypto.createHash('sha256').update(secret).digest();`","message":"The result of `sharedKey` (X25519 output) is a raw shared secret that should *not* be used directly as a cryptographic key. It must be processed with a strong one-way function, such as HKDF or SHA-256, before use in symmetric encryption to ensure security and prevent direct attacks.","severity":"gotcha","affected_versions":">=0.0.1"},{"fix":"Always provide a cryptographically secure 64-byte random buffer for the `random` argument in `sign` and `signMessage` unless deterministic signatures are explicitly required and understood for your use case. Example: `crypto.randomBytes(64)` in Node.js.","message":"The `sign` and `signMessage` functions accept an optional `random` argument, which *must* be 64 cryptographically secure random bytes if provided. Omitting this argument leads to deterministic signatures, which can be a security risk in some protocols if not explicitly desired and accounted for.","severity":"gotcha","affected_versions":">=0.0.1"},{"fix":"Always convert your message data to a `Uint8Array` or Node.js `Buffer` before passing it to signature or verification functions to ensure predictable cryptographic behavior. Example: `Uint8Array.from(Buffer.from('my message', 'utf8'))`.","message":"The `message` parameter in `sign`, `verify`, `signMessage`, and `openMessage` is typed as `any`. While flexible, cryptographic functions typically expect messages as `Uint8Array` or `Buffer` to ensure consistent and secure hashing. Passing arbitrary types could lead to unexpected internal conversions or vulnerabilities.","severity":"gotcha","affected_versions":">=0.0.1"},{"fix":"Carefully consider your keying strategy. If full Ed25519 compatibility is paramount and X25519 is only for key agreement, use standard Ed25519 keys and convert them for X25519 operations with a dedicated converter library.","message":"This library uses X25519 keys for both signing and key agreement, embedding a sign bit into the signature for verification. If your application primarily uses Ed25519 keys and you only need X25519 for key agreement, it might be simpler and potentially more interoperable to use an Ed25519 library and an external conversion utility (like `ed2curve-js`) rather than relying on `curve25519-js`'s unique signing approach.","severity":"gotcha","affected_versions":">=0.0.1"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"For browser environments, ensure you have a `Buffer` polyfill (e.g., install `buffer` from npm and configure your bundler, like Webpack 5+, to alias it) or use a browser-native alternative for hex string to Uint8Array conversion.","cause":"Attempting to use `Buffer.from` in a browser environment without a compatible polyfill or shim. `Buffer` is a Node.js global.","error":"TypeError: Buffer is not defined"},{"fix":"Always ensure your private and public keys are 32-byte `Uint8Array` instances. Verify the source and conversion process for your key material.","cause":"Cryptographic functions require specific byte lengths for keys. Providing a `Uint8Array` of incorrect length (e.g., 64 bytes instead of 32 for a Curve25519 key) will cause this error.","error":"Error: Expected Uint8Array of length 32 for privateKey (or publicKey)"},{"fix":"Double-check the integrity of the message, ensure the correct public key corresponds to the private key used for signing, and confirm the signature has not been altered. Verify the inputs (`publicKey`, `message`, `signature`) are all correct and in the expected `Uint8Array` format.","cause":"The provided signature does not match the message and public key. This can be due to message tampering, incorrect public key, incorrect signature, or issues with the random data used during signing (if applicable).","error":"Error: Signature verification failed."}],"ecosystem":"npm"}