nostr-helper.ts raw
1 /* eslint-disable @typescript-eslint/no-unused-vars */
2 import { bech32 } from '@scure/base';
3 import * as utils from '@noble/curves/abstract/utils';
4 import { getPublicKey } from 'nostr-tools';
5 import { encrypt as nip49Encrypt } from 'nostr-tools/nip49';
6
7 export interface NostrHexObject {
8 represents: string;
9 hex: string;
10 }
11
12 export interface NostrPubkeyObject {
13 hex: string;
14 npub: string;
15 }
16
17 export interface NostrPrivkeyObject {
18 hex: string;
19 nsec: string;
20 }
21
22 export class NostrHelper {
23 static getNostrPrivkeyObject(nsec_OR_hex: string): NostrPrivkeyObject {
24 // 1. Assume we got an nsec.
25 // Try to generate hex value.
26 try {
27 const hexObject = this.#nSomething2hexObject(nsec_OR_hex);
28 if (hexObject.represents !== 'nsec') {
29 throw new Error('The provided string is NOT an nsec.');
30 }
31
32 // Everything is fine. The provided string IS an nsec.
33 return {
34 hex: hexObject.hex,
35 nsec: nsec_OR_hex,
36 };
37 } catch (error) {
38 // Continue.
39 }
40
41 // 2. Assume we got an hex.
42 // Try to generate the nsec.
43 try {
44 const nsec = NostrHelper.privkey2nsec(nsec_OR_hex);
45 return {
46 hex: nsec_OR_hex,
47 nsec,
48 };
49 } catch (error) {
50 // Continue;
51 }
52
53 throw new Error('Could not convert the provided string into nsec/hex.');
54 }
55
56 static getNostrPubkeyObject(npub_OR_hex: string): NostrPubkeyObject {
57 // 1. Assume we got an npub.
58 // Try to generate hex value.
59 try {
60 const hexObject = this.#nSomething2hexObject(npub_OR_hex);
61 if (hexObject.represents !== 'npub') {
62 throw new Error('The provided string is NOT an npub.');
63 }
64
65 // Everything is fine. The provided string IS an npub.
66 return {
67 hex: hexObject.hex,
68 npub: npub_OR_hex,
69 };
70 } catch (error) {
71 // Continue.
72 }
73
74 // 2. Assume we got an hex.
75 // Try to generate the npub.
76 try {
77 const npub = NostrHelper.pubkey2npub(npub_OR_hex);
78 return {
79 hex: npub_OR_hex,
80 npub,
81 };
82 } catch (error) {
83 // Continue;
84 }
85
86 throw new Error('Could not convert the provided string into npub/hex.');
87 }
88
89 static pubkey2npub(hex: string): string {
90 const data = utils.hexToBytes(hex);
91 const words = bech32.toWords(data);
92 return bech32.encode('npub', words, 5000);
93 }
94
95 static privkey2nsec(hex: string): string {
96 const data = utils.hexToBytes(hex);
97 const words = bech32.toWords(data);
98 return bech32.encode('nsec', words, 5000);
99 }
100
101 static pubkeyFromPrivkey(hex: string): string {
102 const privkeyBytes = utils.hexToBytes(hex);
103 return getPublicKey(privkeyBytes);
104 }
105
106 static hex2bytes(hex: string): Uint8Array {
107 return utils.hexToBytes(hex);
108 }
109
110 static splitKey(text: string, first: number, last: number): string {
111 const part1 = text.slice(0, first);
112 const part2 = '...';
113 const part3 = text.slice(-last);
114 return `${part1}${part2}${part3}`;
115 }
116
117 static #nSomething2hexObject(nSomething: string): NostrHexObject {
118 const { prefix, words } = bech32.decode(
119 nSomething as `${string}1${string}`,
120 5000
121 );
122 const data = new Uint8Array(bech32.fromWords(words));
123
124 return {
125 represents: prefix,
126 hex: utils.bytesToHex(data),
127 };
128 }
129
130 /**
131 * Encrypts a private key (hex) with a password using NIP-49.
132 * Returns an ncryptsec bech32 string.
133 * @param privkeyHex - The private key in hex format
134 * @param password - The password to encrypt with
135 * @param logN - Optional log2(N) parameter for scrypt (default: 16)
136 * @returns Promise<string> - The ncryptsec bech32 encoded encrypted key
137 */
138 static async privkeyToNcryptsec(
139 privkeyHex: string,
140 password: string,
141 logN = 16
142 ): Promise<string> {
143 const privkeyBytes = utils.hexToBytes(privkeyHex);
144 return nip49Encrypt(privkeyBytes, password, logN);
145 }
146 }
147