From b86de671e1740ee713a4787230fb165d25f4bdc6 Mon Sep 17 00:00:00 2001 From: Mothana Date: Mon, 12 Feb 2024 17:45:32 +0400 Subject: [PATCH] don't log privateKey, log nprofile --- src/custom-nip19.ts | 88 +++++++++++++++++++++++++++++++++++ src/services/nostr/handler.ts | 14 +++++- 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/custom-nip19.ts diff --git a/src/custom-nip19.ts b/src/custom-nip19.ts new file mode 100644 index 00000000..62c7c56a --- /dev/null +++ b/src/custom-nip19.ts @@ -0,0 +1,88 @@ +/* + This file contains functions that deal with encoding and decoding nprofiles, + but with he addition of bridge urls in the nprofile. + These functions are basically the same functions from nostr-tools package + but with some tweaks to allow for the bridge inclusion. +*/ +import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils'; +import { bech32 } from 'bech32'; + +export const utf8Decoder = new TextDecoder('utf-8') +export const utf8Encoder = new TextEncoder() + + +export type CustomProfilePointer = { + pubkey: string + relays?: string[] + bridge?: string[] // one bridge +} + + + +type TLV = { [t: number]: Uint8Array[] } + + +const encodeTLV = (tlv: TLV): Uint8Array => { + const entries: Uint8Array[] = [] + + Object.entries(tlv) + /* + the original function does a reverse() here, + but here it causes the nprofile string to be different, + even though it would still decode to the correct original inputs + */ + //.reverse() + .forEach(([t, vs]) => { + vs.forEach(v => { + const entry = new Uint8Array(v.length + 2) + entry.set([parseInt(t)], 0) + entry.set([v.length], 1) + entry.set(v, 2) + entries.push(entry) + }) + }) + return concatBytes(...entries); +} + +export const encodeNprofile = (profile: CustomProfilePointer): string => { + const data = encodeTLV({ + 0: [hexToBytes(profile.pubkey)], + 1: (profile.relays || []).map(url => utf8Encoder.encode(url)), + 2: (profile.bridge || []).map(url => utf8Encoder.encode(url)) + }); + const words = bech32.toWords(data) + return bech32.encode("nprofile", words, 5000); +} + +const parseTLV = (data: Uint8Array): TLV => { + const result: TLV = {} + let rest = data + while (rest.length > 0) { + const t = rest[0] + const l = rest[1] + const v = rest.slice(2, 2 + l) + rest = rest.slice(2 + l) + if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`) + result[t] = result[t] || [] + result[t].push(v) + } + return result +} + +export const decodeNprofile = (nprofile: string): CustomProfilePointer => { + const { prefix, words } = bech32.decode(nprofile, 5000) + if (prefix !== "nprofile") { + throw new Error ("Expected nprofile prefix"); + } + const data = new Uint8Array(bech32.fromWords(words)) + + const tlv = parseTLV(data); + if (!tlv[0]?.[0]) throw new Error('missing TLV 0 for nprofile') + if (tlv[0][0].length !== 32) throw new Error('TLV 0 should be 32 bytes') + + return { + pubkey: bytesToHex(tlv[0][0]), + relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [], + bridge: tlv[2] ? tlv[2].map(d => utf8Decoder.decode(d)): [] + } +} \ No newline at end of file diff --git a/src/services/nostr/handler.ts b/src/services/nostr/handler.ts index ffd94424..6bb1fce2 100644 --- a/src/services/nostr/handler.ts +++ b/src/services/nostr/handler.ts @@ -2,6 +2,7 @@ import { SimplePool, Sub, Event, UnsignedEvent, getEventHash, finishEvent, relayInit } from './tools/index.js' import { encryptData, decryptData, getSharedSecret, decodePayload, encodePayload } from './nip44.js' import { getLogger } from '../helpers/logger.js' +import { encodeNprofile } from '../../custom-nip19.js' const handledEvents: string[] = [] // TODO: - big memory leak here, add TTL type AppInfo = { appId: string, publicKey: string, privateKey: string, name: string } export type SendData = { type: "content", content: string, pub: string } | { type: "event", event: UnsignedEvent } @@ -88,7 +89,18 @@ export default class Handler { eventCallback: (event: NostrEvent) => void constructor(settings: NostrSettings, eventCallback: (event: NostrEvent) => void) { this.settings = settings - console.log(settings) + console.log( + { + ...settings, + apps: settings.apps.map(app => { + const { privateKey, ...rest } = app; + return { + ...rest, + nprofile: encodeNprofile({ pubkey: rest.publicKey, relays: settings.relays }) + } + }) + } + ) this.eventCallback = eventCallback this.settings.apps.forEach(app => { this.apps[app.publicKey] = app