ed25519-keygen
Generate ed25519 keys for SSH, PGP (GPG), TOR, IPNS and SLIP-0010 hdkey.
- Pure JS, no CLI tools are involved
- Can generate both deterministic and random keys
- Uses noble-curves under the hood
Includes SLIP-0010 (ed BIP32) HDKey implementation, funded by the Kin Foundation for
Kinetic. For the apps made with the library, check out:
terminal7 WebRTC terminal multiplexer
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 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;
}
utils
import { randomBytes } from 'ed25519-keygen/utils';
const key = randomBytes(32);
CSPRNG for secure generation of random Uint8Array. Utilizes webcrypto under the hood.
License
MIT (c) Paul Miller (https://paulmillr.com), see LICENSE file.