ed25519-keygen
Generate ed25519 keys for SSH, PGP (GPG), TOR, IPNS and SLIP-0010 hdkey.
Generation is deterministic and done in pure javascript, without CLI tools.
Uses noble-curves under the hood.
Includes SLIP-0010 / BIP32 HDKey implementation,
sponsored by the Kin Foundation for Kinetic.
For the apps made with the library, check out:
Usage
npm install ed25519-keygen
The package exports six modules:
Use it in the following way:
import ssh from 'ed25519-keygen/ssh';
import pgp from 'ed25519-keygen/pgp';
import tor from 'ed25519-keygen/tor';
import { randomBytes } from 'ed25519-keygen/utils';
ssh(seed, username)
seed: Uint8Array
username: string
- Returns
{ fingerprint: string, privateKey: string, publicKey: string, publicKeyBytes: Uint8Array }
import ssh from 'ed25519-keygen/ssh';
import { randomBytes } from 'ed25519-keygen/utils';
const sseed = randomBytes(32);
const skeys = await ssh(sseed, 'user@example.com');
console.log(skeys.fingerprint);
console.log(skeys.privateKey);
console.log(skeys.publicKey);
pgp(seed, user, password)
seed: Uint8Array
user: string
password: string
createdAt: number
- (default: 0) timestamp corresponding to key creation time- Returns
{ keyId: string, privateKey: string, publicKey: string, publicKeyBytes: Uint8Array }
Creates keys compatible with GPG. GPG is a commonly known utility that supports PGP protocol. Quirks:
- Generated private and public keys would have different representation,
however, their fingerprints would be the same. This is because AES encryption is used to
hide the keys, and AES requires different IV / salt.
- The function is slow (~725ms on Apple M1), because it uses S2K to derive keys.
- "warning: lower 3 bits of the secret key are not cleared"
happens even for keys generated with GnuPG 2.3.6, because check looks at item as Opaque MPI, when it is just MPI: see bugtracker URL.
import * as pgp from 'ed25519-keygen/pgp';
import { randomBytes } from 'ed25519-keygen/utils';
const pseed = randomBytes(32);
const pkeys = await pgp.getKeys(pseed, 'user@example.com', 'password');
console.log(pkeys.keyId);
console.log(pkeys.privateKey);
console.log(pkeys.publicKey);
console.log(await pgp.pubArmor.decode(keys.publicKey));
const privDecoded = await pgp.privArmor.decode(keys.privateKey);
console.log(privDecoded);
console.log({
ed25519: await pgp.decodeSecretKey('password', privDecoded[0].data),
cv25519: await pgp.decodeSecretKey('password', privDecoded[3].data),
});
tor(seed)
Generates TOR addresses.
seed: Uint8Array
- Returns
{ privateKey: string, publicKey: string, publicKeyBytes: Uint8Array }
import tor from 'ed25519-keygen/tor';
import { randomBytes } from 'ed25519-keygen/utils';
const tseed = randomBytes(32);
const tkeys = await tor(tseed);
console.log(tkeys.privateKey);
console.log(tkeys.publicKey);
ipns(seed)
Generates IPNS addresses.
seed: Uint8Array
- Returns
{ privateKey: string, publicKey: string, base36: string, base32: string, base16: string, contenthash: string}
import ipns from 'ed25519-keygen/ipns';
import { randomBytes } from 'ed25519-keygen/utils';
const iseed = randomBytes(32);
const ikeys = await ipns(iseed);
console.log(ikeys.privateKey);
console.log(ikeys.publicKey);
console.log(ikeys.base16);
console.log(ikeys.base32);
console.log(ikeys.base36);
console.log(ikeys.contenthash);
hdkey
SLIP-0010 hierarchical deterministic (HD) wallets for implementation. Based on audited code from scure-bip32. Check out scure-bip39 if you also need mnemonic phrases.
- SLIP-0010 publicKey is 33 bytes (see this issue), if you want 32-byte publicKey, use
.publicKeyRaw
getter - SLIP-0010 vectors fingerprint is actually
parentFingerprint
- SLIP-0010 doesn't allow deriving non-hardened keys for Ed25519, however some other libraries treat non-hardened keys (
m/0/1
) as hardened (m/0'/1'
). If you want this behaviour, there is a flag forceHardened
in derive
method
import { HDKey } from 'ed25519-keygen/hdkey';
const hdkey1 = HDKey.fromMasterSeed(seed);
[hdkey1.depth, hdkey1.index, hdkey1.chainCode];
console.log(hdkey2.privateKey, hdkey2.publicKey);
console.log(hdkey3.derive("m/0/2147483647'/1'"));
const sig = hdkey3.sign(hash);
hdkey3.verify(hash, sig);
Note: chainCode
property is essentially a private part
of a secret "master" key, it should be guarded from unauthorized access.
The full API is:
class HDKey {
public static HARDENED_OFFSET: number;
public static fromMasterSeed(seed: Uint8Array | string): HDKey;
readonly depth: number = 0;
readonly index: number = 0;
readonly chainCode: Uint8Array | null = null;
readonly parentFingerprint: number = 0;
public readonly privateKey: Uint8Array;
get fingerprint(): number;
get fingerprintHex(): string;
get parentFingerprintHex(): string;
get pubKeyHash(): Uint8Array;
get publicKey(): Uint8Array;
get publicKeyRaw(): Uint8Array;
derive(path: string, forceHardened = false): HDKey;
deriveChild(index: number): HDKey;
sign(hash: Uint8Array): Uint8Array;
verify(hash: Uint8Array, signature: Uint8Array): boolean;
}
randomBytes(length)
byteLength: number
default is 32
- Returns
Uint8Array
filled with cryptographically secure random bytes
License
MIT (c) Paul Miller (https://paulmillr.com), see LICENSE file.