Coder Social home page Coder Social logo

paulmillr / ed25519-keygen Goto Github PK

View Code? Open in Web Editor NEW
28.0 3.0 6.0 118 KB

Generate ed25519 keys for SSH, PGP (GPG), TOR, IPNS and SLIP-0010 hdkey

License: MIT License

JavaScript 47.32% TypeScript 52.68%
bip32 ed25519 hdkey pgp slip0010 ssh tor ipns

ed25519-keygen's Introduction

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 ipns from 'ed25519-keygen/ipns';
import { HDKey } from 'ed25519-keygen/hdkey';
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 = ssh(sseed, '[email protected]');
console.log(skeys.fingerprint);
console.log(skeys.privateKey);
console.log(skeys.publicKey);
/*
SHA256:3M832z6j5R6mQh4TTzVG5KVs2IbvythcS6VPiEixMJg
-----BEGIN OPENSSH PRIVATE KEY-----

b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACB7IzMcWzDbGACJFPmt8hDZGedH6W1w0SGuY1Ut+oIlxQAAAJh8wUpUfMFK
VAAAAAtzc2gtZWQyNTUxOQAAACB7IzMcWzDbGACJFPmt8hDZGedH6W1w0SGuY1Ut+oIlxQ
AAAEBPTJHsreF9Losr930Yt/8DseFi66G7vK8QF/Kd8fcRlXsjMxxbMNsYAIkU+a3yENkZ
50fpbXDRIa5jVS36giXFAAAAEHVzZXJAZXhhbXBsZS5jb20BAgMEBQ==
-----END OPENSSH PRIVATE KEY-----

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHsjMxxbMNsYAIkU+a3yENkZ50fpbXDRIa5jVS36giXF [email protected]
*/

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:

  1. 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.
  2. The function is slow (~725ms on Apple M1), because it uses S2K to derive keys.
  3. "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 = pgp.getKeys(pseed, '[email protected]', 'password');
console.log(pkeys.keyId);
console.log(pkeys.privateKey);
console.log(pkeys.publicKey);
/*
ca88e2a8afd9cdb8
-----BEGIN PGP PRIVATE KEY BLOCK-----

lIYEAAAAABYJKwYBBAHaRw8BAQdA0TSxOgyxDIuJh0afj457vpf7IZJsnyVu+HG2
k/v1F0P+BwMCpkzMdFodxiHwgVurmhm72ikz5FqdF8WJEBy0VC8ovbXkNz9oCi31
grwUafRgb874q0n99Q6Kh1cDMwMNF6vjQvgusaJQtvy75Y0pkNEnKrQQdXNlckBl
eGFtcGxlLmNvbYiUBBMWCgA8FiEELCHHdWxwPnwIbwzfyojiqK/ZzbgFAgAAAAAC
GwMFCwkIBwIDIgIBBhUKCQgLAgQWAgMBAh4HAheAAAoJEMqI4qiv2c24nyABALvn
+XR0T4AeohBNL+h88o2tgPazB1GtKo1FhMb8cpaDAQCRr8Ml3Ow5ijijFBQ0aqG4
1D43SIinNvQFD59o85YfBZyLBAAAAAASCisGAQQBl1UBBQEBB0C8acmhByJtlAZo
7T2lVQa0iCo0RBm/CgMJKO+3/NaHGgMBCAf+BwMCJsLfz4M3/KrwyBBBRu8MTvjq
pY5FjFcJGoPRhYHX+/ZATZf4cRrA0LX3zDi1nudO1f755Q4ALWjPXNXMMBkmKjHJ
p5WaAFm7xMdxEvXYaIh4BBgWCgAgFiEELCHHdWxwPnwIbwzfyojiqK/ZzbgFAgAA
AAACGwwACgkQyojiqK/ZzbikkgEAod4RMOLsVngAH/WFBWQi+Ee5hZ4nXsfasDsT
hNEnaqcBAI5h/ss8SU/gOmx/uiGJTCpp8VRAac+VbeU5XU//aLwA
=oOli
-----END PGP PRIVATE KEY BLOCK-----

-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEAAAAABYJKwYBBAHaRw8BAQdA0TSxOgyxDIuJh0afj457vpf7IZJsnyVu+HG2
k/v1F0O0EHVzZXJAZXhhbXBsZS5jb22IlAQTFgoAPBYhBCwhx3VscD58CG8M38qI
4qiv2c24BQIAAAAAAhsDBQsJCAcCAyICAQYVCgkICwIEFgIDAQIeBwIXgAAKCRDK
iOKor9nNuJ8gAQC75/l0dE+AHqIQTS/ofPKNrYD2swdRrSqNRYTG/HKWgwEAka/D
JdzsOYo4oxQUNGqhuNQ+N0iIpzb0BQ+faPOWHwW4OAQAAAAAEgorBgEEAZdVAQUB
AQdAvGnJoQcibZQGaO09pVUGtIgqNEQZvwoDCSjvt/zWhxoDAQgHiHgEGBYKACAW
IQQsIcd1bHA+fAhvDN/KiOKor9nNuAUCAAAAAAIbDAAKCRDKiOKor9nNuKSSAQCh
3hEw4uxWeAAf9YUFZCL4R7mFnidex9qwOxOE0SdqpwEAjmH+yzxJT+A6bH+6IYlM
KmnxVEBpz5Vt5TldT/9ovAA=
=4hZe
-----END PGP PUBLIC KEY BLOCK-----
*/

// Also, you can explore existing keys internal structure
console.log(pgp.pubArmor.decode(keys.publicKey));
const privDecoded = pgp.privArmor.decode(keys.privateKey);
console.log(privDecoded);
// And receive raw private keys as bigint
console.log({
  ed25519: pgp.decodeSecretKey('password', privDecoded[0].data),
  cv25519: 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 = tor(tseed);
console.log(tkeys.privateKey);
console.log(tkeys.publicKey);
/*
ED25519-V3:EOl78M2gARYOyp4BDltfzxSR3dA/LLTXZLb2imgOwFuYC5ISIUxsQ42ywzHaxvc03mahmaLziuyN0+f8EhM+4w==
rx724x3oambzxr46pkbdckdqyut5x5lhsneru3uditf4nuyuf4uou6qd.onion
*/

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 = 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);
/*
0x080112400681d6420abb1ba47acd5c03c8e5ee84185a2673576b262e234e50c46d86f59712c8299ec2c51dffbbcb4f9fccadcee1424cb237e9b30d3cd72d47c18103689d
0x017200240801122012c8299ec2c51dffbbcb4f9fccadcee1424cb237e9b30d3cd72d47c18103689d
ipns://f017200240801122012c8299ec2c51dffbbcb4f9fccadcee1424cb237e9b30d3cd72d47c18103689d
ipns://bafzaajaiaejcaewifgpmfri57654wt47zsw45ykcjszdp2ntbu6nolkhygaqg2e5
ipns://k51qzi5uqu5dgnfwbc46une4upw1vc9hxznymyeykmg6rev1513yrnbyrwmmql
0xe501017200240801122012c8299ec2c51dffbbcb4f9fccadcee1424cb237e9b30d3cd72d47c18103689d
*/

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);

// props
[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.

ed25519-keygen's People

Contributors

0xc0de4c0ffee avatar luisosta avatar paulmillr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ed25519-keygen's Issues

IPNS keygen support

๐Ÿ™ it'd be nice to have ed25519 IPNS (libp2p-key) keygen support.

This is our experimental app-specific deterministic keygen format using deterministic ETH signatures for ENS contenthash.

let caip10 = `eip155:1:${App.user.address}`
let domain = "domain.eth"
let info = `${caip10}:${domain}`
let password = "pass12#$" // optional salt/pin
let extradata = await sha256(`${info}:${password ? password : ''}`) //still testing 
let msg = `Requesting Signature To Generate IPNS Keys For ${domain}\n\nWARNING:Do Not Sign This Request From Untrusted Clients\nExtradata: ${extradata}\nSigned By: ${caip10}`;
let sig = await App.user.signMessage(msg);
let inputKey = sha256(
    hexToBytes(
        sig.toLowerCase().startsWith('0x') ? sig.slice(2) : sig
    )
)
let salt = await sha256(`${info}:${password ? password : ''}:${sig.slice(-64)}`)
let hashKey = await hkdf(sha256, inputKey, salt, info, 42)
let privateKey = hashToPrivateScalar(hashKey, ed25519.CURVE.n, true).toString(16).padStart(64, "0")
let publicKey = bytesToHex(await ed25519.getPublicKey(privateKey))
let key = `08011240${privateKey}${publicKey}`
let w3Name = await Name.from(hexToBytes(key))
// let contenthash = `0xe5010172002408011220{$publicKey}`

ssh is not support in macos

import ssh from 'ed25519-keygen/ssh';
...

I used the example, to generate a ssh private & public, for macos there will be an error

-> % ssh-add id_ed25519
Error loading key "id_ed25519": invalid format

Is it possible to implement extended private/public keys?

The library https://github.com/paulmillr/scure-bip32/blob/main/index.ts implements extended keys where you can do hdkey.fromExtendedKey(master.publicExtendedKey).deriveChild(x)

I'm looking for this feature using ed25519 and found this nice hdkeys implementation: https://github.com/paulmillr/ed25519-keygen/blob/main/src/hdkey.ts it looks very similar to scure-bip32, I've tried porting over the code from scure-bip32 to use ed25519 but not to much success, so I figured I should just ask. Is this possible at all?

Update `@noble/curves` or make dependencies matching less strict

Hello,

I've faced with the fact that clean installation of ed25519-keygen module installs two versions of @noble/hashes.

npm install ed25519-keygen

npm list @noble/hashes

[email protected] test-ed25519-keygen
โ””โ”€โ”ฌ [email protected]
  โ”œโ”€โ”ฌ @noble/[email protected]
  โ”‚ โ””โ”€โ”€ @noble/[email protected]
  โ””โ”€โ”€ @noble/[email protected]

It is not a big problem for a single usage but produces a lot of duplicates when using many @noble/* and @scure/* libraries.

doesn't seem as though ed25519-keygen/utils.js exists in npm deployment

error importing utils:

node:internal/errors:484
    ErrorCaptureStackTrace(err);
    ^

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/jcolson/src/personal/gpgssh/node_modules/ed25519-keygen/utils.js'

ls output of node_modules:

ll node_modules/ed25519-keygen/
.rw-r--r--   46 jcolson staff 30 Aug 21:28 index.d.ts
.rw-r--r--  101 jcolson staff 30 Aug 21:28 index.d.ts.map
.rw-r--r--   87 jcolson staff 30 Aug 21:28 index.js
.rw-r--r-- 1.8k jcolson staff 30 Aug 21:28 package.json
.rw-r--r-- 2.3k jcolson staff 30 Aug 21:28 pgp.d.ts
.rw-r--r-- 1.0k jcolson staff 30 Aug 21:28 pgp.d.ts.map
.rw-r--r--  20k jcolson staff 30 Aug 21:28 pgp.js
.rw-r--r-- 5.8k jcolson staff 30 Aug 21:28 README.md
drwxr-xr-x    - jcolson staff 30 Aug 21:28 src
.rw-r--r-- 1.5k jcolson staff 30 Aug 21:28 ssh.d.ts
.rw-r--r--  741 jcolson staff 30 Aug 21:28 ssh.d.ts.map
.rw-r--r-- 2.5k jcolson staff 30 Aug 21:28 ssh.js
.rw-r--r--  339 jcolson staff 30 Aug 21:28 tor.d.ts
.rw-r--r--  258 jcolson staff 30 Aug 21:28 tor.d.ts.map
.rw-r--r-- 1.3k jcolson staff 30 Aug 21:28 tor.js

does this lbrary supports non-hardened derivation of a public key ?

I am looking in generated keys from a parent hd key both from its private key and public key, so that the resulting keys matches

The api seems to only work on keys where the private key is known. any pointer to get non-hardened keys ?

I want something like the function deriveFromPublicKey in the following

derive(parent private key).publickey = deriveFromPublicKey(public key)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.