diff --git a/README.md b/README.md index 8103dd3..9244b99 100644 --- a/README.md +++ b/README.md @@ -67,4 +67,6 @@ pool.addRelay('') // will automatically subscribe to the all the events called with .sub above ``` +All functions expect bytearrays as hex strings and output bytearrays as hex strings. + For other utils please read the source (for now). diff --git a/event.js b/event.js index 9207131..e6a6f6f 100644 --- a/event.js +++ b/event.js @@ -1,7 +1,6 @@ import {Buffer} from 'buffer' -import * as secp256k1 from '@noble/secp256k1' - -import {sha256} from './utils' +import createHash from 'create-hash' +import {signSchnorr, verifySchnorr} from 'tiny-secp256k1' export function getBlankEvent() { return { @@ -24,20 +23,24 @@ export function serializeEvent(evt) { ]) } -export async function getEventHash(event) { - let eventHash = await sha256(Buffer.from(serializeEvent(event))) +export function getEventHash(event) { + let eventHash = createHash('sha256') + .update(Buffer.from(serializeEvent(event))) + .digest() return Buffer.from(eventHash).toString('hex') } -export async function verifySignature(event) { - return await secp256k1.schnorr.verify( - event.sig, - await getEventHash(event), - event.pubkey +export function verifySignature(event) { + if (event.id !== getEventHash(event)) return false + return verifySchnorr( + Buffer.from(event.id, 'hex'), + Buffer.from(event.pubkey, 'hex') + Buffer.from(event.sig, 'hex'), ) } -export async function signEvent(event, key) { - let eventHash = await getEventHash(event) - return await secp256k1.schnorr.sign(eventHash, key) +export function signEvent(event, key) { + let eventHash = Buffer.from(getEventHash(event), 'hex') + let key = Buffer.from(key, 'hex') + return Buffer.from(signSchnorr(eventHash, key)).toString('hex') } diff --git a/index.js b/index.js index cdeb0da..83bbcb3 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,4 @@ +import {generatePrivateKey, getPublicKey} from './keys' import {relayConnect} from './relay' import {relayPool} from './pool' import { @@ -8,7 +9,6 @@ import { getEventHash } from './event' import {matchFilter, matchFilters} from './filter' -import {makeRandom32, sha256, getPublicKey} from './utils' export { relayConnect, @@ -17,8 +17,6 @@ export { verifySignature, serializeEvent, getEventHash, - makeRandom32, - sha256, getPublicKey, getBlankEvent, matchFilter, diff --git a/keys.js b/keys.js new file mode 100644 index 0000000..65be95f --- /dev/null +++ b/keys.js @@ -0,0 +1,19 @@ +import randomBytes from 'randombytes' +import {isPrivate, pointFromScalar} from 'tiny-secp256k1' + +export function generatePrivateKey() { + let i = 8 + while (i--) { + let r32 = Buffer.from(randomBytes(32)) + if (isPrivate(r32)) return r32.toString('hex') + } + throw new Error( + 'Valid private key was not found in 8 iterations. PRNG is broken' + ) +} + +export function getPublicKey(privateKey) { + return Buffer.from( + pointFromScalar(Buffer.from(privateKey, 'hex'), true) + ).toString('hex') +} diff --git a/nip06.js b/nip06.js index c4d96a8..0122807 100644 --- a/nip06.js +++ b/nip06.js @@ -1,17 +1,28 @@ import createHmac from 'create-hmac' -import randomBytes from 'randombytes' -import * as bip39 from 'bip39' +import {wordlist} from 'micro-bip39/wordlists/english' +import { + generateMnemonic, + mnemonicToSeedSync, + validateMnemonic +} from 'micro-bip39' +import BIP32Factory from 'bip32' +import * as ecc from 'tiny-secp256k1' + +const bip32 = BIP32Factory(ecc) export function privateKeyFromSeed(seed) { - let hmac = createHmac('sha512', Buffer.from('Nostr seed', 'utf8')) - hmac.update(seed) - return hmac.digest().slice(0, 32).toString('hex') + let root = bip32.fromSeed(Buffer.from(seed, 'hex')) + return root.derivePath(`m/44'/1237'/0'/0'`).privateKey.toString('hex') } export function seedFromWords(mnemonic) { - return bip39.mnemonicToSeedSync(mnemonic) + return Buffer.from(mnemonicToSeedSync(mnemonic, wordlist)).toString('hex') } export function generateSeedWords() { - return bip39.entropyToMnemonic(randomBytes(16).toString('hex')) + return generateMnemonic(wordlist) +} + +export function validateWords(words) { + return validateMnemonic(words, wordlist) } diff --git a/package.json b/package.json index 9428707..5dfd4b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "nostr-tools", - "version": "0.11.0", + "version": "0.12.1", "description": "Tools for making a Nostr client.", "repository": { "type": "git", @@ -8,12 +8,15 @@ }, "dependencies": { "@noble/secp256k1": "^1.3.0", - "bip39": "^3.0.4", + "bip32": "^3.0.1", "browserify-cipher": ">=1", "buffer": ">=5", + "create-hash": "^1.2.0", "create-hmac": ">=1", "dns-packet": "^5.2.4", + "micro-bip39": "^0.1.3", "randombytes": ">=2", + "tiny-secp256k1": "^2.1.2", "websocket-polyfill": "^0.0.3" }, "keywords": [ diff --git a/utils.js b/utils.js deleted file mode 100644 index ece6c0f..0000000 --- a/utils.js +++ /dev/null @@ -1,6 +0,0 @@ -import * as secp256k1 from '@noble/secp256k1' - -export const makeRandom32 = () => secp256k1.utils.randomPrivateKey() -export const sha256 = m => secp256k1.utils.sha256(Uint8Array.from(m)) -export const getPublicKey = privateKey => - secp256k1.schnorr.getPublicKey(privateKey)