@qntx/nostr 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abort-Ba8tDrOI.mjs +38 -0
- package/dist/abort-Ba8tDrOI.mjs.map +1 -0
- package/dist/{builder-BphJa-Y-.d.mts → builder-BWn55Paf.d.mts} +20 -14
- package/dist/{builder-q0VsvnYE.mjs → builder-CyrGKRVP.mjs} +32 -16
- package/dist/builder-CyrGKRVP.mjs.map +1 -0
- package/dist/{client-Bd8B1mYm.mjs → client-BWsX9Cv1.mjs} +354 -195
- package/dist/client-BWsX9Cv1.mjs.map +1 -0
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/core.d.mts +8 -8
- package/dist/core.mjs +8 -9
- package/dist/error-BW7re1UN.d.mts +9 -0
- package/dist/error-B__OMBRY.mjs +14 -0
- package/dist/error-B__OMBRY.mjs.map +1 -0
- package/dist/{error-WEFS-R-4.d.mts → error-CR8Urc84.d.mts} +20 -8
- package/dist/{error-CwBxle6N.mjs → error-DrEIM7FO.mjs} +5 -3
- package/dist/error-DrEIM7FO.mjs.map +1 -0
- package/dist/{event-CzoQEFMF.mjs → event-D8Nfoh4K.mjs} +40 -32
- package/dist/event-D8Nfoh4K.mjs.map +1 -0
- package/dist/{event-B5D2jxt_.d.mts → event-fU3Y01PR.d.mts} +23 -20
- package/dist/{filter-BxwjEkZp.mjs → filter-BQl3GlHt.mjs} +42 -35
- package/dist/filter-BQl3GlHt.mjs.map +1 -0
- package/dist/filter-C0IMByE3.d.mts +37 -0
- package/dist/{gossip-C5RVB1UB.d.mts → gossip-BRQwIM_q.d.mts} +25 -23
- package/dist/{gossip-3xkRRCm_.mjs → gossip-oloDMWEj.mjs} +52 -34
- package/dist/gossip-oloDMWEj.mjs.map +1 -0
- package/dist/gossip.d.mts +1 -1
- package/dist/gossip.mjs +1 -1
- package/dist/{http-6vevbBlu.d.mts → http-C4HwIu4B.d.mts} +7 -7
- package/dist/{http-D2rhokZo.mjs → http-CuazNRIP.mjs} +10 -6
- package/dist/http-CuazNRIP.mjs.map +1 -0
- package/dist/index-B425CAcQ.d.mts +328 -0
- package/dist/index-BVyfUBuG.d.mts +181 -0
- package/dist/{index-BYMnhRIC.d.mts → index-Ba7bJ7DD.d.mts} +68 -50
- package/dist/{index-DaRyjo7O.d.mts → index-Boe8FshM.d.mts} +13 -7
- package/dist/index-CUIvFQdE.d.mts +73 -0
- package/dist/index-JdaS_uce.d.mts +207 -0
- package/dist/index.d.mts +22 -20
- package/dist/index.mjs +19 -18
- package/dist/{key-Dn-aX4x4.mjs → key-Bbtys6o7.mjs} +22 -14
- package/dist/key-Bbtys6o7.mjs.map +1 -0
- package/dist/{key-BiGxGLhg.d.mts → key-BlsYhN1V.d.mts} +9 -6
- package/dist/{kind-BLl4cLOc.mjs → kind-Cxwhhkx8.mjs} +2 -1
- package/dist/kind-Cxwhhkx8.mjs.map +1 -0
- package/dist/{loaders-BNQszDAn.mjs → loaders-o6YXB_Ri.mjs} +329 -305
- package/dist/loaders-o6YXB_Ri.mjs.map +1 -0
- package/dist/loaders.d.mts +3 -3
- package/dist/loaders.mjs +2 -2
- package/dist/memory-C2EVx0_O.mjs +46 -0
- package/dist/memory-C2EVx0_O.mjs.map +1 -0
- package/dist/{memory-BCmhZPek.mjs → memory-index-Do1mz0WF.mjs} +212 -82
- package/dist/memory-index-Do1mz0WF.mjs.map +1 -0
- package/dist/{message-C_Uj-Y-c.mjs → message-DcY6Fi9o.mjs} +38 -24
- package/dist/message-DcY6Fi9o.mjs.map +1 -0
- package/dist/{message-DJC-Jz6T.d.mts → message-iGPIqnn0.d.mts} +15 -8
- package/dist/{nip17-BWq-Kfq5.d.mts → nip17-DNEkz8rM.d.mts} +18 -17
- package/dist/{nip19-D7905oIv.d.mts → nip19-BevCJZsp.d.mts} +23 -3
- package/dist/{nip46-C_DoxiS_.d.mts → nip46-DDV4dC56.d.mts} +13 -12
- package/dist/{nip59-DljW_sYh.d.mts → nip59-zQgdLcEr.d.mts} +28 -25
- package/dist/{nip65-Di5hZRuV.d.mts → nip65-DBfh2smt.d.mts} +8 -3
- package/dist/{nip77-ByxlgoQO.d.mts → nip77-DH4RhS5r.d.mts} +9 -8
- package/dist/nips/blossom.d.mts +37 -37
- package/dist/nips/blossom.mjs +67 -52
- package/dist/nips/blossom.mjs.map +1 -1
- package/dist/nips/nip04.d.mts +2 -3
- package/dist/nips/nip04.mjs +6 -10
- package/dist/nips/nip04.mjs.map +1 -1
- package/dist/nips/nip05.d.mts +37 -43
- package/dist/nips/nip05.mjs +57 -57
- package/dist/nips/nip05.mjs.map +1 -1
- package/dist/nips/nip10.d.mts +18 -19
- package/dist/nips/nip10.mjs +76 -61
- package/dist/nips/nip10.mjs.map +1 -1
- package/dist/nips/nip11.d.mts +9 -8
- package/dist/nips/nip11.mjs +14 -11
- package/dist/nips/nip11.mjs.map +1 -1
- package/dist/nips/nip13.d.mts +9 -9
- package/dist/nips/nip13.mjs +15 -17
- package/dist/nips/nip13.mjs.map +1 -1
- package/dist/nips/nip17.d.mts +1 -1
- package/dist/nips/nip17.mjs +17 -12
- package/dist/nips/nip17.mjs.map +1 -1
- package/dist/nips/nip19.d.mts +1 -1
- package/dist/nips/nip19.mjs +52 -29
- package/dist/nips/nip19.mjs.map +1 -1
- package/dist/nips/nip21.d.mts +5 -6
- package/dist/nips/nip21.mjs +8 -7
- package/dist/nips/nip21.mjs.map +1 -1
- package/dist/nips/nip27.d.mts +7 -8
- package/dist/nips/nip27.mjs +26 -25
- package/dist/nips/nip27.mjs.map +1 -1
- package/dist/nips/nip42.d.mts +5 -6
- package/dist/nips/nip42.mjs +3 -3
- package/dist/nips/nip42.mjs.map +1 -1
- package/dist/nips/nip44.d.mts +24 -7
- package/dist/nips/nip44.mjs +25 -15
- package/dist/nips/nip44.mjs.map +1 -1
- package/dist/nips/nip46.d.mts +1 -1
- package/dist/nips/nip46.mjs +52 -49
- package/dist/nips/nip46.mjs.map +1 -1
- package/dist/nips/nip49.d.mts +8 -7
- package/dist/nips/nip49.mjs +16 -12
- package/dist/nips/nip49.mjs.map +1 -1
- package/dist/nips/nip51.d.mts +25 -27
- package/dist/nips/nip51.mjs +47 -36
- package/dist/nips/nip51.mjs.map +1 -1
- package/dist/nips/nip57.d.mts +12 -13
- package/dist/nips/nip57.mjs +35 -38
- package/dist/nips/nip57.mjs.map +1 -1
- package/dist/nips/nip59.d.mts +1 -1
- package/dist/nips/nip59.mjs +14 -18
- package/dist/nips/nip59.mjs.map +1 -1
- package/dist/nips/nip65.d.mts +1 -1
- package/dist/nips/nip65.mjs +11 -8
- package/dist/nips/nip65.mjs.map +1 -1
- package/dist/nips/nip77.d.mts +1 -1
- package/dist/nips/nip77.mjs +56 -34
- package/dist/nips/nip77.mjs.map +1 -1
- package/dist/nips/nip96.d.mts +21 -12
- package/dist/nips/nip96.mjs +59 -36
- package/dist/nips/nip96.mjs.map +1 -1
- package/dist/nips/nip98.d.mts +12 -12
- package/dist/nips/nip98.mjs +18 -19
- package/dist/nips/nip98.mjs.map +1 -1
- package/dist/{pool-C_j1OmBB.mjs → pool-XmAGaufS.mjs} +611 -487
- package/dist/pool-XmAGaufS.mjs.map +1 -0
- package/dist/pool-tEr7c5bG.d.mts +302 -0
- package/dist/reactive-DDlThDSZ.mjs +324 -0
- package/dist/reactive-DDlThDSZ.mjs.map +1 -0
- package/dist/reactive-DFpqtIf_.d.mts +66 -0
- package/dist/relay.d.mts +5 -4
- package/dist/relay.mjs +2 -2
- package/dist/report-CHld4uWj.mjs +32 -0
- package/dist/report-CHld4uWj.mjs.map +1 -0
- package/dist/{signer-CZ6K6FUc.mjs → signer-CKR9EHce.mjs} +107 -72
- package/dist/signer-CKR9EHce.mjs.map +1 -0
- package/dist/signer.d.mts +3 -3
- package/dist/signer.mjs +3 -2
- package/dist/storage-xXxLGCVb.mjs +1376 -0
- package/dist/storage-xXxLGCVb.mjs.map +1 -0
- package/dist/storage.d.mts +4 -4
- package/dist/storage.mjs +5 -4
- package/dist/store.d.mts +2 -0
- package/dist/store.mjs +2 -0
- package/dist/testing.d.mts +125 -0
- package/dist/testing.mjs +957 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-2pCUprRU.d.mts +47 -0
- package/dist/types-CRQEk0Oi.d.mts +19 -0
- package/dist/util-LfQirgDk.mjs +113 -0
- package/dist/util-LfQirgDk.mjs.map +1 -0
- package/dist/wasm.d.mts +4 -4
- package/dist/wasm.mjs +54 -55
- package/dist/wasm.mjs.map +1 -1
- package/dist/websocket-CmeqvAkp.d.mts +27 -0
- package/package.json +31 -9
- package/dist/builder-q0VsvnYE.mjs.map +0 -1
- package/dist/client-Bd8B1mYm.mjs.map +0 -1
- package/dist/error-B49rbDzT.d.mts +0 -7
- package/dist/error-CwBxle6N.mjs.map +0 -1
- package/dist/error-DZEs5OuZ.mjs +0 -24
- package/dist/error-DZEs5OuZ.mjs.map +0 -1
- package/dist/event-CzoQEFMF.mjs.map +0 -1
- package/dist/filter-BxwjEkZp.mjs.map +0 -1
- package/dist/filter-DUEKP3q1.d.mts +0 -40
- package/dist/gossip-3xkRRCm_.mjs.map +0 -1
- package/dist/http-D2rhokZo.mjs.map +0 -1
- package/dist/index-C0C4e-Gn.d.mts +0 -55
- package/dist/index-DgOugrpF.d.mts +0 -275
- package/dist/index-EwL6fHLo.d.mts +0 -267
- package/dist/index-k2YhCgsh.d.mts +0 -57
- package/dist/key-Dn-aX4x4.mjs.map +0 -1
- package/dist/kind-BLl4cLOc.mjs.map +0 -1
- package/dist/loaders-BNQszDAn.mjs.map +0 -1
- package/dist/memory-BCmhZPek.mjs.map +0 -1
- package/dist/message-C_Uj-Y-c.mjs.map +0 -1
- package/dist/pool-C_j1OmBB.mjs.map +0 -1
- package/dist/pool-DhRi7l-b.d.mts +0 -297
- package/dist/signer-CZ6K6FUc.mjs.map +0 -1
- package/dist/storage-DOy0iLRE.mjs +0 -870
- package/dist/storage-DOy0iLRE.mjs.map +0 -1
- package/dist/types-BzyADqnw.d.mts +0 -19
- package/dist/types-Dn24TPFs.d.mts +0 -43
- package/dist/util-6tfLQ9JF.mjs +0 -73
- package/dist/util-6tfLQ9JF.mjs.map +0 -1
package/dist/nips/nip42.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as Kind } from "../kind-
|
|
1
|
+
import { t as Kind } from "../kind-Cxwhhkx8.mjs";
|
|
2
2
|
//#region src/nips/nip42.ts
|
|
3
3
|
/**
|
|
4
|
-
* Build an unsigned NIP-42 AUTH event template for the given relay challenge.
|
|
5
|
-
*
|
|
4
|
+
* Build an unsigned NIP-42 AUTH event template for the given relay challenge. Caller must sign with
|
|
5
|
+
* their `NostrSigner` / keys.
|
|
6
6
|
*/
|
|
7
7
|
function makeAuthEvent(relayURL, challenge) {
|
|
8
8
|
return {
|
package/dist/nips/nip42.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip42.mjs","names":[],"sources":["../../src/nips/nip42.ts"],"sourcesContent":["import type { EventTemplate } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\n\n/**\n * Build an unsigned NIP-42 AUTH event template for the given relay challenge
|
|
1
|
+
{"version":3,"file":"nip42.mjs","names":[],"sources":["../../src/nips/nip42.ts"],"sourcesContent":["import type { EventTemplate } from \"../core/event.ts\";\nimport { Kind } from \"../core/kind.ts\";\n\n/**\n * Build an unsigned NIP-42 AUTH event template for the given relay challenge. Caller must sign with\n * their `NostrSigner` / keys.\n */\nexport function makeAuthEvent(relayURL: string, challenge: string): EventTemplate {\n return {\n kind: Kind.ClientAuth,\n created_at: Math.floor(Date.now() / 1000),\n tags: [\n [\"relay\", relayURL],\n [\"challenge\", challenge],\n ],\n content: \"\",\n };\n}\n\n/** True when a CLOSED reason requests NIP-42 authentication. */\nexport function isAuthRequired(reason: string): boolean {\n return reason.startsWith(\"auth-required:\");\n}\n"],"mappings":";;;;;;AAOA,SAAgB,cAAc,UAAkB,WAAkC;CAChF,OAAO;EACL,MAAM,KAAK;EACX,YAAY,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI;EACxC,MAAM,CACJ,CAAC,SAAS,QAAQ,GAClB,CAAC,aAAa,SAAS,CACzB;EACA,SAAS;CACX;AACF;;AAGA,SAAgB,eAAe,QAAyB;CACtD,OAAO,OAAO,WAAW,gBAAgB;AAC3C"}
|
package/dist/nips/nip44.d.mts
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
//#region src/nips/nip44.d.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Default decode-side payload cap: the base64 length of a payload carrying exactly 1 MiB (0x100000
|
|
4
|
+
* bytes) of plaintext — version(1) + nonce(32) + extended prefix(6) + calcPaddedLen(1 MiB) +
|
|
5
|
+
* mac(32) bytes. NIP-44 asks implementations to enforce their own maximum payload size; relays cap
|
|
6
|
+
* events far below this. Raise it via `opts.maxPayloadChars` in `decrypt`/ `decryptFromPubkey` for
|
|
7
|
+
* large local payloads.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_MAX_PAYLOAD_CHARS: number;
|
|
10
|
+
export declare function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array;
|
|
11
|
+
/** NIP-44 `get_message_keys`: HKDF-expand the 76-byte key material. */
|
|
12
|
+
export declare function getMessageKeys(conversationKey: Uint8Array, nonce: Uint8Array): {
|
|
13
|
+
chacha_key: Uint8Array;
|
|
14
|
+
chacha_nonce: Uint8Array;
|
|
15
|
+
hmac_key: Uint8Array;
|
|
16
|
+
};
|
|
17
|
+
export declare function calcPaddedLen(len: number): number;
|
|
18
|
+
export declare function encrypt(plaintext: string, conversationKey: Uint8Array, nonce?: Uint8Array): string;
|
|
19
|
+
export declare function decrypt(payload: string, conversationKey: Uint8Array, opts?: {
|
|
20
|
+
maxPayloadChars?: number;
|
|
21
|
+
}): string;
|
|
6
22
|
/** Convenience: derive conversation key from secret/public pair and encrypt. */
|
|
7
|
-
declare function encryptToPubkey(plaintext: string, secretKey: Uint8Array, peerPubkey: string): string;
|
|
23
|
+
export declare function encryptToPubkey(plaintext: string, secretKey: Uint8Array, peerPubkey: string): string;
|
|
8
24
|
/** Convenience: decrypt a payload from a peer pubkey. */
|
|
9
|
-
declare function decryptFromPubkey(payload: string, secretKey: Uint8Array, peerPubkey: string
|
|
25
|
+
export declare function decryptFromPubkey(payload: string, secretKey: Uint8Array, peerPubkey: string, opts?: {
|
|
26
|
+
maxPayloadChars?: number;
|
|
27
|
+
}): string;
|
|
10
28
|
//#endregion
|
|
11
|
-
export { calcPaddedLen, decrypt, decryptFromPubkey, encrypt, encryptToPubkey, getConversationKey };
|
|
12
29
|
//# sourceMappingURL=nip44.d.mts.map
|
package/dist/nips/nip44.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { a as hexToBytes$1, d as utf8Encoder, f as CryptoError, n as assertHex32, r as assertSecretKeyBytes, u as utf8Decoder } from "../util-LfQirgDk.mjs";
|
|
2
|
+
import { concatBytes, randomBytes } from "@noble/hashes/utils.js";
|
|
3
3
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
4
4
|
import { secp256k1 } from "@noble/curves/secp256k1.js";
|
|
5
|
-
import { concatBytes, randomBytes } from "@noble/hashes/utils.js";
|
|
6
5
|
import { base64 } from "@scure/base";
|
|
7
6
|
import { chacha20 } from "@noble/ciphers/chacha.js";
|
|
8
7
|
import { equalBytes } from "@noble/ciphers/utils.js";
|
|
@@ -12,15 +11,24 @@ import { hmac } from "@noble/hashes/hmac.js";
|
|
|
12
11
|
const minPlaintextSize = 1;
|
|
13
12
|
const maxPlaintextSize = 4294967295;
|
|
14
13
|
const extendedPrefixThreshold = 65536;
|
|
14
|
+
/**
|
|
15
|
+
* Default decode-side payload cap: the base64 length of a payload carrying exactly 1 MiB (0x100000
|
|
16
|
+
* bytes) of plaintext — version(1) + nonce(32) + extended prefix(6) + calcPaddedLen(1 MiB) +
|
|
17
|
+
* mac(32) bytes. NIP-44 asks implementations to enforce their own maximum payload size; relays cap
|
|
18
|
+
* events far below this. Raise it via `opts.maxPayloadChars` in `decrypt`/ `decryptFromPubkey` for
|
|
19
|
+
* large local payloads.
|
|
20
|
+
*/
|
|
21
|
+
const DEFAULT_MAX_PAYLOAD_CHARS = Math.ceil((71 + calcPaddedLen(1048576)) / 3) * 4;
|
|
15
22
|
function assert32(bytes, label) {
|
|
16
23
|
if (bytes.length !== 32) throw new CryptoError(`${label} must be 32 bytes`);
|
|
17
24
|
}
|
|
18
25
|
function getConversationKey(privkeyA, pubkeyB) {
|
|
19
26
|
assertSecretKeyBytes(privkeyA);
|
|
20
27
|
assertHex32(pubkeyB, "public key");
|
|
21
|
-
const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes(
|
|
28
|
+
const sharedX = secp256k1.getSharedSecret(privkeyA, hexToBytes$1(`02${pubkeyB.toLowerCase()}`)).subarray(1, 33);
|
|
22
29
|
return extract(sha256, sharedX, utf8Encoder.encode("nip44-v2"));
|
|
23
30
|
}
|
|
31
|
+
/** NIP-44 `get_message_keys`: HKDF-expand the 76-byte key material. */
|
|
24
32
|
function getMessageKeys(conversationKey, nonce) {
|
|
25
33
|
assert32(conversationKey, "conversation_key");
|
|
26
34
|
assert32(nonce, "nonce");
|
|
@@ -79,18 +87,20 @@ function hmacAad(key, message, aad) {
|
|
|
79
87
|
if (aad.length !== 32) throw new CryptoError("AAD associated data must be 32 bytes");
|
|
80
88
|
return hmac(sha256, key, concatBytes(aad, message));
|
|
81
89
|
}
|
|
82
|
-
function decodePayload(payload) {
|
|
90
|
+
function decodePayload(payload, maxPayloadChars) {
|
|
83
91
|
if (typeof payload !== "string") throw new CryptoError("payload must be a valid string");
|
|
84
|
-
if (payload.
|
|
85
|
-
if (payload
|
|
92
|
+
if (payload.startsWith("#")) throw new CryptoError("unknown encryption version");
|
|
93
|
+
if (payload.length < 132 || payload.length > maxPayloadChars) throw new CryptoError(`invalid payload length: ${payload.length}`);
|
|
86
94
|
let data;
|
|
87
95
|
try {
|
|
88
96
|
data = base64.decode(payload);
|
|
89
97
|
} catch (error) {
|
|
90
|
-
throw new CryptoError(
|
|
98
|
+
throw new CryptoError(`invalid base64: ${error instanceof Error ? error.message : "decode failed"}`);
|
|
91
99
|
}
|
|
92
|
-
if (data.length < 99) throw new CryptoError(
|
|
93
|
-
|
|
100
|
+
if (data.length < 99) throw new CryptoError(`invalid data length: ${data.length}`);
|
|
101
|
+
const version = data.at(0);
|
|
102
|
+
if (version === void 0) throw new CryptoError("invalid data length");
|
|
103
|
+
if (version !== 2) throw new CryptoError(`unknown encryption version ${version}`);
|
|
94
104
|
return {
|
|
95
105
|
nonce: data.subarray(1, 33),
|
|
96
106
|
ciphertext: data.subarray(33, -32),
|
|
@@ -104,8 +114,8 @@ function encrypt(plaintext, conversationKey, nonce = randomBytes(32)) {
|
|
|
104
114
|
const mac = hmacAad(hmac_key, ciphertext, nonce);
|
|
105
115
|
return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac));
|
|
106
116
|
}
|
|
107
|
-
function decrypt(payload, conversationKey) {
|
|
108
|
-
const { nonce, ciphertext, mac } = decodePayload(payload);
|
|
117
|
+
function decrypt(payload, conversationKey, opts) {
|
|
118
|
+
const { nonce, ciphertext, mac } = decodePayload(payload, opts?.maxPayloadChars ?? DEFAULT_MAX_PAYLOAD_CHARS);
|
|
109
119
|
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
|
|
110
120
|
const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);
|
|
111
121
|
if (!equalBytes(calculatedMac, mac)) throw new CryptoError("invalid MAC");
|
|
@@ -116,10 +126,10 @@ function encryptToPubkey(plaintext, secretKey, peerPubkey) {
|
|
|
116
126
|
return encrypt(plaintext, getConversationKey(secretKey, peerPubkey));
|
|
117
127
|
}
|
|
118
128
|
/** Convenience: decrypt a payload from a peer pubkey. */
|
|
119
|
-
function decryptFromPubkey(payload, secretKey, peerPubkey) {
|
|
120
|
-
return decrypt(payload, getConversationKey(secretKey, peerPubkey));
|
|
129
|
+
function decryptFromPubkey(payload, secretKey, peerPubkey, opts) {
|
|
130
|
+
return decrypt(payload, getConversationKey(secretKey, peerPubkey), opts);
|
|
121
131
|
}
|
|
122
132
|
//#endregion
|
|
123
|
-
export { calcPaddedLen, decrypt, decryptFromPubkey, encrypt, encryptToPubkey, getConversationKey };
|
|
133
|
+
export { DEFAULT_MAX_PAYLOAD_CHARS, calcPaddedLen, decrypt, decryptFromPubkey, encrypt, encryptToPubkey, getConversationKey, getMessageKeys };
|
|
124
134
|
|
|
125
135
|
//# sourceMappingURL=nip44.mjs.map
|
package/dist/nips/nip44.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip44.mjs","names":["hkdf_extract","hkdf_expand"],"sources":["../../src/nips/nip44.ts"],"sourcesContent":["import { chacha20 } from \"@noble/ciphers/chacha.js\";\nimport { equalBytes } from \"@noble/ciphers/utils.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { extract as hkdf_extract, expand as hkdf_expand } from \"@noble/hashes/hkdf.js\";\nimport { hmac } from \"@noble/hashes/hmac.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { concatBytes, randomBytes } from \"@noble/hashes/utils.js\";\nimport { base64 } from \"@scure/base\";\nimport { CryptoError } from \"../core/error.ts\";\nimport {\n assertHex32,\n assertSecretKeyBytes,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n} from \"../core/util.ts\";\n\nconst minPlaintextSize = 0x0001;\nconst maxPlaintextSize = 0xffffffff;\nconst extendedPrefixThreshold = 0x10000;\n\nfunction assert32(bytes: Uint8Array, label: string): void {\n if (bytes.length !== 32) throw new CryptoError(`${label} must be 32 bytes`);\n}\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n assertSecretKeyBytes(privkeyA);\n assertHex32(pubkeyB, \"public key\");\n const sharedX = secp256k1\n .getSharedSecret(privkeyA, hexToBytes(\"02\" + pubkeyB.toLowerCase()))\n .subarray(1, 33);\n return hkdf_extract(sha256, sharedX, utf8Encoder.encode(\"nip44-v2\"));\n}\n\nfunction getMessageKeys(\n conversationKey: Uint8Array,\n nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n assert32(conversationKey, \"conversation_key\");\n assert32(nonce, \"nonce\");\n const keys = hkdf_expand(sha256, conversationKey, nonce, 76);\n return {\n chacha_key: keys.subarray(0, 32),\n chacha_nonce: keys.subarray(32, 44),\n hmac_key: keys.subarray(44, 76),\n };\n}\n\nexport function calcPaddedLen(len: number): number {\n if (!Number.isSafeInteger(len) || len < 1) throw new CryptoError(\"expected positive integer\");\n if (len <= 32) return 32;\n const nextPower = 2 ** (Math.floor(Math.log2(len - 1)) + 1);\n const chunk = nextPower <= 256 ? 32 : nextPower / 8;\n return chunk * (Math.floor((len - 1) / chunk) + 1);\n}\n\nfunction writeU16BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > 0xffff) {\n throw new CryptoError(\"invalid plaintext size: must be between 1 and 65535 bytes\");\n }\n const arr = new Uint8Array(2);\n new DataView(arr.buffer).setUint16(0, num, false);\n return arr;\n}\n\nfunction writeU32BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < extendedPrefixThreshold || num > maxPlaintextSize) {\n throw new CryptoError(\"invalid plaintext size: must be between 65536 and 4294967295 bytes\");\n }\n const arr = new Uint8Array(4);\n new DataView(arr.buffer).setUint32(0, num, false);\n return arr;\n}\n\nfunction pad(plaintext: string): Uint8Array {\n const unpadded = utf8Encoder.encode(plaintext);\n const unpaddedLen = unpadded.length;\n if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize) {\n throw new CryptoError(\"invalid plaintext size: must be between 1 and 4294967295 bytes\");\n }\n const prefix =\n unpaddedLen >= extendedPrefixThreshold\n ? concatBytes(new Uint8Array([0, 0]), writeU32BE(unpaddedLen))\n : writeU16BE(unpaddedLen);\n const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen);\n return concatBytes(prefix, unpadded, suffix);\n}\n\nfunction unpad(padded: Uint8Array): string {\n const dv = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);\n const firstTwo = dv.getUint16(0);\n let unpaddedLen: number;\n let prefixLen: number;\n if (firstTwo === 0) {\n unpaddedLen = dv.getUint32(2);\n if (unpaddedLen < extendedPrefixThreshold) throw new CryptoError(\"invalid padding\");\n prefixLen = 6;\n } else {\n unpaddedLen = firstTwo;\n prefixLen = 2;\n }\n const unpadded = padded.subarray(prefixLen, prefixLen + unpaddedLen);\n if (\n unpaddedLen < minPlaintextSize ||\n unpaddedLen > maxPlaintextSize ||\n unpadded.length !== unpaddedLen ||\n padded.length !== prefixLen + calcPaddedLen(unpaddedLen)\n ) {\n throw new CryptoError(\"invalid padding\");\n }\n return utf8Decoder.decode(unpadded);\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n if (aad.length !== 32) throw new CryptoError(\"AAD associated data must be 32 bytes\");\n return hmac(sha256, key, concatBytes(aad, message));\n}\n\nfunction decodePayload(payload: string): {\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n mac: Uint8Array;\n} {\n if (typeof payload !== \"string\") throw new CryptoError(\"payload must be a valid string\");\n if (payload.length < 132) throw new CryptoError(\"invalid payload length: \" + payload.length);\n if (payload[0] === \"#\") throw new CryptoError(\"unknown encryption version\");\n let data: Uint8Array;\n try {\n data = base64.decode(payload);\n } catch (error) {\n throw new CryptoError(\n \"invalid base64: \" + (error instanceof Error ? error.message : \"decode failed\"),\n );\n }\n if (data.length < 99) throw new CryptoError(\"invalid data length: \" + data.length);\n if (data[0] !== 2) throw new CryptoError(\"unknown encryption version \" + data[0]);\n return {\n nonce: data.subarray(1, 33),\n ciphertext: data.subarray(33, -32),\n mac: data.subarray(-32),\n };\n}\n\nexport function encrypt(\n plaintext: string,\n conversationKey: Uint8Array,\n nonce: Uint8Array = randomBytes(32),\n): string {\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const padded = pad(plaintext);\n const ciphertext = chacha20(chacha_key, chacha_nonce, padded);\n const mac = hmacAad(hmac_key, ciphertext, nonce);\n return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac));\n}\n\nexport function decrypt(payload: string, conversationKey: Uint8Array): string {\n const { nonce, ciphertext, mac } = decodePayload(payload);\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);\n if (!equalBytes(calculatedMac, mac)) throw new CryptoError(\"invalid MAC\");\n const padded = chacha20(chacha_key, chacha_nonce, ciphertext);\n return unpad(padded);\n}\n\n/** Convenience: derive conversation key from secret/public pair and encrypt. */\nexport function encryptToPubkey(\n plaintext: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n return encrypt(plaintext, getConversationKey(secretKey, peerPubkey));\n}\n\n/** Convenience: decrypt a payload from a peer pubkey. */\nexport function decryptFromPubkey(\n payload: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n return decrypt(payload, getConversationKey(secretKey, peerPubkey));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nip44.mjs","names":["hexToBytes","hkdf_extract","hkdf_expand"],"sources":["../../src/nips/nip44.ts"],"sourcesContent":["import { chacha20 } from \"@noble/ciphers/chacha.js\";\nimport { equalBytes } from \"@noble/ciphers/utils.js\";\nimport { secp256k1 } from \"@noble/curves/secp256k1.js\";\nimport { extract as hkdf_extract, expand as hkdf_expand } from \"@noble/hashes/hkdf.js\";\nimport { hmac } from \"@noble/hashes/hmac.js\";\nimport { sha256 } from \"@noble/hashes/sha2.js\";\nimport { concatBytes, randomBytes } from \"@noble/hashes/utils.js\";\nimport { base64 } from \"@scure/base\";\n\nimport { CryptoError } from \"../core/error.ts\";\nimport {\n assertHex32,\n assertSecretKeyBytes,\n hexToBytes,\n utf8Decoder,\n utf8Encoder,\n} from \"../core/util.ts\";\n\nconst minPlaintextSize = 0x0001;\nconst maxPlaintextSize = 0xffffffff;\nconst extendedPrefixThreshold = 0x10000;\n\n/**\n * Default decode-side payload cap: the base64 length of a payload carrying exactly 1 MiB (0x100000\n * bytes) of plaintext — version(1) + nonce(32) + extended prefix(6) + calcPaddedLen(1 MiB) +\n * mac(32) bytes. NIP-44 asks implementations to enforce their own maximum payload size; relays cap\n * events far below this. Raise it via `opts.maxPayloadChars` in `decrypt`/ `decryptFromPubkey` for\n * large local payloads.\n */\nexport const DEFAULT_MAX_PAYLOAD_CHARS: number = Math.ceil((71 + calcPaddedLen(0x100000)) / 3) * 4;\n\nfunction assert32(bytes: Uint8Array, label: string): void {\n if (bytes.length !== 32) {\n throw new CryptoError(`${label} must be 32 bytes`);\n }\n}\n\nexport function getConversationKey(privkeyA: Uint8Array, pubkeyB: string): Uint8Array {\n assertSecretKeyBytes(privkeyA);\n assertHex32(pubkeyB, \"public key\");\n const sharedX = secp256k1\n .getSharedSecret(privkeyA, hexToBytes(`02${pubkeyB.toLowerCase()}`))\n .subarray(1, 33);\n return hkdf_extract(sha256, sharedX, utf8Encoder.encode(\"nip44-v2\"));\n}\n\n/** NIP-44 `get_message_keys`: HKDF-expand the 76-byte key material. */\nexport function getMessageKeys(\n conversationKey: Uint8Array,\n nonce: Uint8Array,\n): { chacha_key: Uint8Array; chacha_nonce: Uint8Array; hmac_key: Uint8Array } {\n assert32(conversationKey, \"conversation_key\");\n assert32(nonce, \"nonce\");\n const keys = hkdf_expand(sha256, conversationKey, nonce, 76);\n return {\n chacha_key: keys.subarray(0, 32),\n chacha_nonce: keys.subarray(32, 44),\n hmac_key: keys.subarray(44, 76),\n };\n}\n\nexport function calcPaddedLen(len: number): number {\n if (!Number.isSafeInteger(len) || len < 1) {\n throw new CryptoError(\"expected positive integer\");\n }\n if (len <= 32) {\n return 32;\n }\n const nextPower = 2 ** (Math.floor(Math.log2(len - 1)) + 1);\n const chunk = nextPower <= 256 ? 32 : nextPower / 8;\n return chunk * (Math.floor((len - 1) / chunk) + 1);\n}\n\nfunction writeU16BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < minPlaintextSize || num > 0xffff) {\n throw new CryptoError(\"invalid plaintext size: must be between 1 and 65535 bytes\");\n }\n const arr = new Uint8Array(2);\n new DataView(arr.buffer).setUint16(0, num, false);\n return arr;\n}\n\nfunction writeU32BE(num: number): Uint8Array {\n if (!Number.isSafeInteger(num) || num < extendedPrefixThreshold || num > maxPlaintextSize) {\n throw new CryptoError(\"invalid plaintext size: must be between 65536 and 4294967295 bytes\");\n }\n const arr = new Uint8Array(4);\n new DataView(arr.buffer).setUint32(0, num, false);\n return arr;\n}\n\nfunction pad(plaintext: string): Uint8Array {\n const unpadded = utf8Encoder.encode(plaintext);\n const unpaddedLen = unpadded.length;\n if (unpaddedLen < minPlaintextSize || unpaddedLen > maxPlaintextSize) {\n throw new CryptoError(\"invalid plaintext size: must be between 1 and 4294967295 bytes\");\n }\n const prefix =\n unpaddedLen >= extendedPrefixThreshold\n ? concatBytes(new Uint8Array([0, 0]), writeU32BE(unpaddedLen))\n : writeU16BE(unpaddedLen);\n const suffix = new Uint8Array(calcPaddedLen(unpaddedLen) - unpaddedLen);\n return concatBytes(prefix, unpadded, suffix);\n}\n\nfunction unpad(padded: Uint8Array): string {\n const dv = new DataView(padded.buffer, padded.byteOffset, padded.byteLength);\n const firstTwo = dv.getUint16(0);\n let unpaddedLen: number;\n let prefixLen: number;\n if (firstTwo === 0) {\n unpaddedLen = dv.getUint32(2);\n if (unpaddedLen < extendedPrefixThreshold) {\n throw new CryptoError(\"invalid padding\");\n }\n prefixLen = 6;\n } else {\n unpaddedLen = firstTwo;\n prefixLen = 2;\n }\n const unpadded = padded.subarray(prefixLen, prefixLen + unpaddedLen);\n if (\n unpaddedLen < minPlaintextSize ||\n unpaddedLen > maxPlaintextSize ||\n unpadded.length !== unpaddedLen ||\n padded.length !== prefixLen + calcPaddedLen(unpaddedLen)\n ) {\n throw new CryptoError(\"invalid padding\");\n }\n return utf8Decoder.decode(unpadded);\n}\n\nfunction hmacAad(key: Uint8Array, message: Uint8Array, aad: Uint8Array): Uint8Array {\n if (aad.length !== 32) {\n throw new CryptoError(\"AAD associated data must be 32 bytes\");\n }\n return hmac(sha256, key, concatBytes(aad, message));\n}\n\nfunction decodePayload(\n payload: string,\n maxPayloadChars: number,\n): {\n nonce: Uint8Array;\n ciphertext: Uint8Array;\n mac: Uint8Array;\n} {\n if (typeof payload !== \"string\") {\n throw new CryptoError(\"payload must be a valid string\");\n }\n if (payload.startsWith(\"#\")) {\n throw new CryptoError(\"unknown encryption version\");\n }\n if (payload.length < 132 || payload.length > maxPayloadChars) {\n throw new CryptoError(`invalid payload length: ${payload.length}`);\n }\n let data: Uint8Array;\n try {\n data = base64.decode(payload);\n } catch (error) {\n throw new CryptoError(\n `invalid base64: ${error instanceof Error ? error.message : \"decode failed\"}`,\n );\n }\n if (data.length < 99) {\n throw new CryptoError(`invalid data length: ${data.length}`);\n }\n const version = data.at(0);\n if (version === undefined) {\n throw new CryptoError(\"invalid data length\");\n }\n if (version !== 2) {\n throw new CryptoError(`unknown encryption version ${version}`);\n }\n return {\n nonce: data.subarray(1, 33),\n ciphertext: data.subarray(33, -32),\n mac: data.subarray(-32),\n };\n}\n\nexport function encrypt(\n plaintext: string,\n conversationKey: Uint8Array,\n nonce: Uint8Array = randomBytes(32),\n): string {\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const padded = pad(plaintext);\n const ciphertext = chacha20(chacha_key, chacha_nonce, padded);\n const mac = hmacAad(hmac_key, ciphertext, nonce);\n return base64.encode(concatBytes(new Uint8Array([2]), nonce, ciphertext, mac));\n}\n\nexport function decrypt(\n payload: string,\n conversationKey: Uint8Array,\n opts?: { maxPayloadChars?: number },\n): string {\n const { nonce, ciphertext, mac } = decodePayload(\n payload,\n opts?.maxPayloadChars ?? DEFAULT_MAX_PAYLOAD_CHARS,\n );\n const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);\n const calculatedMac = hmacAad(hmac_key, ciphertext, nonce);\n if (!equalBytes(calculatedMac, mac)) {\n throw new CryptoError(\"invalid MAC\");\n }\n const padded = chacha20(chacha_key, chacha_nonce, ciphertext);\n return unpad(padded);\n}\n\n/** Convenience: derive conversation key from secret/public pair and encrypt. */\nexport function encryptToPubkey(\n plaintext: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n): string {\n return encrypt(plaintext, getConversationKey(secretKey, peerPubkey));\n}\n\n/** Convenience: decrypt a payload from a peer pubkey. */\nexport function decryptFromPubkey(\n payload: string,\n secretKey: Uint8Array,\n peerPubkey: string,\n opts?: { maxPayloadChars?: number },\n): string {\n return decrypt(payload, getConversationKey(secretKey, peerPubkey), opts);\n}\n"],"mappings":"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"}
|
package/dist/nips/nip46.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as Nip46Request, c as createNostrConnectURI, d as encodeNip46Request, f as encodeNip46Response, h as toBunkerURL, i as Nip46Error, l as decodeNip46Request, m as parseNostrConnectURI, n as BunkerPointer, o as Nip46Response, p as parseBunkerURL, r as ClientMetadata, s as NostrConnectParams, t as BUNKER_REGEX, u as decodeNip46Response } from "../nip46-
|
|
1
|
+
import { a as Nip46Request, c as createNostrConnectURI, d as encodeNip46Request, f as encodeNip46Response, h as toBunkerURL, i as Nip46Error, l as decodeNip46Request, m as parseNostrConnectURI, n as BunkerPointer, o as Nip46Response, p as parseBunkerURL, r as ClientMetadata, s as NostrConnectParams, t as BUNKER_REGEX, u as decodeNip46Response } from "../nip46-DDV4dC56.mjs";
|
|
2
2
|
export { BUNKER_REGEX, BunkerPointer, ClientMetadata, Nip46Error, Nip46Request, Nip46Response, NostrConnectParams, createNostrConnectURI, decodeNip46Request, decodeNip46Response, encodeNip46Request, encodeNip46Response, parseBunkerURL, parseNostrConnectURI, toBunkerURL };
|
package/dist/nips/nip46.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as NostrError } from "../error-DZEs5OuZ.mjs";
|
|
1
|
+
import { c as isRecord, g as NostrError, n as assertHex32, o as isHex32 } from "../util-LfQirgDk.mjs";
|
|
3
2
|
//#region src/nips/nip46.ts
|
|
4
3
|
/**
|
|
5
4
|
* NIP-46 (Nostr Connect) protocol helpers: bunker URI, nostrconnect URI, RPC JSON.
|
|
@@ -9,6 +8,7 @@ import { a as NostrError } from "../error-DZEs5OuZ.mjs";
|
|
|
9
8
|
*/
|
|
10
9
|
const BUNKER_REGEX = /^bunker:\/\/([0-9a-fA-F]{64})\??([?/\w:.=&%-]*)$/;
|
|
11
10
|
var Nip46Error = class extends NostrError {
|
|
11
|
+
name = "Nip46Error";
|
|
12
12
|
constructor(message, options) {
|
|
13
13
|
super(message, options);
|
|
14
14
|
}
|
|
@@ -17,40 +17,41 @@ var Nip46Error = class extends NostrError {
|
|
|
17
17
|
function toBunkerURL(pointer) {
|
|
18
18
|
const url = new URL(`bunker://${pointer.pubkey.toLowerCase()}`);
|
|
19
19
|
for (const relay of pointer.relays) url.searchParams.append("relay", relay);
|
|
20
|
-
if (pointer.secret) url.searchParams.set("secret", pointer.secret);
|
|
20
|
+
if (pointer.secret !== void 0 && pointer.secret !== "") url.searchParams.set("secret", pointer.secret);
|
|
21
21
|
return url.toString();
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* Parse a `bunker://` URL into a pointer.
|
|
25
|
-
*
|
|
24
|
+
* Parse a `bunker://` URL into a pointer. Returns undefined when the input is not a bunker URL
|
|
25
|
+
* (including NIP-05 identifiers).
|
|
26
26
|
*/
|
|
27
27
|
function parseBunkerURL(input) {
|
|
28
|
-
const match = input.trim()
|
|
29
|
-
|
|
28
|
+
const match = BUNKER_REGEX.exec(input.trim());
|
|
29
|
+
const hex = match?.at(1);
|
|
30
|
+
if (match === null || hex === void 0) return;
|
|
30
31
|
try {
|
|
31
|
-
const pubkey = assertHex32(
|
|
32
|
-
const qs = new URLSearchParams(match
|
|
32
|
+
const pubkey = assertHex32(hex, "bunker pubkey");
|
|
33
|
+
const qs = new URLSearchParams(match.at(2) ?? "");
|
|
33
34
|
return {
|
|
34
35
|
pubkey,
|
|
35
36
|
relays: qs.getAll("relay"),
|
|
36
|
-
secret: qs.get("secret")
|
|
37
|
+
secret: qs.get("secret") ?? void 0
|
|
37
38
|
};
|
|
38
39
|
} catch {
|
|
39
|
-
return
|
|
40
|
+
return;
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
/** Build a client-initiated `nostrconnect://` URI. */
|
|
43
44
|
function createNostrConnectURI(params) {
|
|
44
|
-
if (!isHex32(params.clientPubkey)) throw new Nip46Error("invalid client pubkey");
|
|
45
|
+
if (!isHex32(params.clientPubkey.toLowerCase())) throw new Nip46Error("invalid client pubkey");
|
|
45
46
|
if (!params.secret) throw new Nip46Error("nostrconnect secret is required");
|
|
46
47
|
if (params.relays.length === 0) throw new Nip46Error("at least one relay is required");
|
|
47
48
|
const query = new URLSearchParams();
|
|
48
49
|
for (const relay of params.relays) query.append("relay", relay);
|
|
49
50
|
query.set("secret", params.secret);
|
|
50
|
-
if (params.perms && params.perms.length > 0) query.set("perms", params.perms.join(","));
|
|
51
|
-
if (params.name) query.set("name", params.name);
|
|
52
|
-
if (params.url) query.set("url", params.url);
|
|
53
|
-
if (params.image) query.set("image", params.image);
|
|
51
|
+
if (params.perms !== void 0 && params.perms.length > 0) query.set("perms", params.perms.join(","));
|
|
52
|
+
if (params.name !== void 0 && params.name !== "") query.set("name", params.name);
|
|
53
|
+
if (params.url !== void 0 && params.url !== "") query.set("url", params.url);
|
|
54
|
+
if (params.image !== void 0 && params.image !== "") query.set("image", params.image);
|
|
54
55
|
return `nostrconnect://${params.clientPubkey.toLowerCase()}?${query.toString()}`;
|
|
55
56
|
}
|
|
56
57
|
/** Parse a `nostrconnect://` URI. */
|
|
@@ -58,26 +59,30 @@ function parseNostrConnectURI(uri) {
|
|
|
58
59
|
let url;
|
|
59
60
|
try {
|
|
60
61
|
url = new URL(uri);
|
|
61
|
-
} catch (
|
|
62
|
-
throw new Nip46Error(`invalid nostrconnect URI: ${uri}`, { cause:
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw new Nip46Error(`invalid nostrconnect URI: ${uri}`, { cause: error instanceof Error ? error : void 0 });
|
|
63
64
|
}
|
|
64
65
|
if (url.protocol !== "nostrconnect:") throw new Nip46Error(`expected nostrconnect: scheme, got ${url.protocol}`);
|
|
65
66
|
const clientPubkey = url.hostname || url.pathname.replace(/^\/*/, "");
|
|
66
|
-
if (!isHex32(clientPubkey)) throw new Nip46Error("invalid client pubkey in nostrconnect URI");
|
|
67
|
+
if (!isHex32(clientPubkey.toLowerCase())) throw new Nip46Error("invalid client pubkey in nostrconnect URI");
|
|
67
68
|
const secret = url.searchParams.get("secret");
|
|
68
|
-
if (
|
|
69
|
+
if (secret === null || secret === "") throw new Nip46Error("missing secret in nostrconnect URI");
|
|
69
70
|
const relays = url.searchParams.getAll("relay");
|
|
70
71
|
if (relays.length === 0) throw new Nip46Error("missing relays in nostrconnect URI");
|
|
71
|
-
const
|
|
72
|
-
return {
|
|
72
|
+
const params = {
|
|
73
73
|
clientPubkey: clientPubkey.toLowerCase(),
|
|
74
74
|
relays,
|
|
75
|
-
secret
|
|
76
|
-
perms: permsRaw ? permsRaw.split(",").filter(Boolean) : void 0,
|
|
77
|
-
name: url.searchParams.get("name") ?? void 0,
|
|
78
|
-
url: url.searchParams.get("url") ?? void 0,
|
|
79
|
-
image: url.searchParams.get("image") ?? void 0
|
|
75
|
+
secret
|
|
80
76
|
};
|
|
77
|
+
const permsRaw = url.searchParams.get("perms");
|
|
78
|
+
if (permsRaw !== null && permsRaw !== "") params.perms = permsRaw.split(",").filter((p) => p !== "");
|
|
79
|
+
const name = url.searchParams.get("name");
|
|
80
|
+
if (name !== null) params.name = name;
|
|
81
|
+
const metadataUrl = url.searchParams.get("url");
|
|
82
|
+
if (metadataUrl !== null) params.url = metadataUrl;
|
|
83
|
+
const image = url.searchParams.get("image");
|
|
84
|
+
if (image !== null) params.image = image;
|
|
85
|
+
return params;
|
|
81
86
|
}
|
|
82
87
|
function encodeNip46Request(req) {
|
|
83
88
|
return JSON.stringify({
|
|
@@ -90,41 +95,39 @@ function decodeNip46Request(json) {
|
|
|
90
95
|
let data;
|
|
91
96
|
try {
|
|
92
97
|
data = JSON.parse(json);
|
|
93
|
-
} catch (
|
|
94
|
-
throw new Nip46Error("invalid NIP-46 request JSON", { cause:
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw new Nip46Error("invalid NIP-46 request JSON", { cause: error instanceof Error ? error : void 0 });
|
|
95
100
|
}
|
|
96
|
-
if (!isRecord(data) || typeof data
|
|
97
|
-
if (!Array.isArray(data
|
|
101
|
+
if (!isRecord(data) || typeof data["id"] !== "string" || typeof data["method"] !== "string") throw new Nip46Error("invalid NIP-46 request shape");
|
|
102
|
+
if (!Array.isArray(data["params"]) || !data["params"].every((p) => typeof p === "string")) throw new Nip46Error("invalid NIP-46 request params");
|
|
98
103
|
return {
|
|
99
|
-
id: data
|
|
100
|
-
method: data
|
|
101
|
-
params: data
|
|
104
|
+
id: data["id"],
|
|
105
|
+
method: data["method"],
|
|
106
|
+
params: data["params"]
|
|
102
107
|
};
|
|
103
108
|
}
|
|
104
109
|
function encodeNip46Response(res) {
|
|
105
110
|
const body = { id: res.id };
|
|
106
|
-
if (res.result !== void 0) body
|
|
107
|
-
if (res.error !== void 0) body
|
|
111
|
+
if (res.result !== void 0) body["result"] = res.result;
|
|
112
|
+
if (res.error !== void 0) body["error"] = res.error;
|
|
108
113
|
return JSON.stringify(body);
|
|
109
114
|
}
|
|
110
115
|
function decodeNip46Response(json) {
|
|
111
116
|
let data;
|
|
112
117
|
try {
|
|
113
118
|
data = JSON.parse(json);
|
|
114
|
-
} catch (
|
|
115
|
-
throw new Nip46Error("invalid NIP-46 response JSON", { cause:
|
|
119
|
+
} catch (error) {
|
|
120
|
+
throw new Nip46Error("invalid NIP-46 response JSON", { cause: error instanceof Error ? error : void 0 });
|
|
116
121
|
}
|
|
117
|
-
if (!isRecord(data) || typeof data
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
function isRecord(value) {
|
|
127
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
122
|
+
if (!isRecord(data) || typeof data["id"] !== "string") throw new Nip46Error("invalid NIP-46 response shape");
|
|
123
|
+
const result = data["result"] === null ? void 0 : data["result"];
|
|
124
|
+
const error = data["error"] === null ? void 0 : data["error"];
|
|
125
|
+
if (result !== void 0 && typeof result !== "string") throw new Nip46Error("invalid NIP-46 response result");
|
|
126
|
+
if (error !== void 0 && typeof error !== "string") throw new Nip46Error("invalid NIP-46 response error");
|
|
127
|
+
const response = { id: data["id"] };
|
|
128
|
+
if (result !== void 0) response.result = result;
|
|
129
|
+
if (error !== void 0) response.error = error;
|
|
130
|
+
return response;
|
|
128
131
|
}
|
|
129
132
|
//#endregion
|
|
130
133
|
export { BUNKER_REGEX, Nip46Error, createNostrConnectURI, decodeNip46Request, decodeNip46Response, encodeNip46Request, encodeNip46Response, parseBunkerURL, parseNostrConnectURI, toBunkerURL };
|
package/dist/nips/nip46.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip46.mjs","names":[],"sources":["../../src/nips/nip46.ts"],"sourcesContent":["/**\n * NIP-46 (Nostr Connect) protocol helpers: bunker URI, nostrconnect URI, RPC JSON.\n * Transport/signing live in {@link Nip46Signer}.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/46.md\n */\nimport { assertHex32, isHex32 } from \"../core/util.ts\";\nimport { NostrError } from \"../core/error.ts\";\n\nexport const BUNKER_REGEX = /^bunker:\\/\\/([0-9a-fA-F]{64})\\??([?/\\w:.=&%-]*)$/;\n\nexport type BunkerPointer = {\n /** Remote signer / bunker public key (hex). */\n pubkey: string;\n relays: string[];\n secret: string | null;\n};\n\nexport type ClientMetadata = {\n name?: string;\n url?: string;\n image?: string;\n};\n\nexport type NostrConnectParams = {\n clientPubkey: string;\n relays: string[];\n secret: string;\n perms?: string[];\n} & ClientMetadata;\n\nexport type Nip46Request = {\n id: string;\n method: string;\n params: string[];\n};\n\nexport type Nip46Response = {\n id: string;\n result?: string;\n error?: string;\n};\n\nexport class Nip46Error extends NostrError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\n/** Encode a bunker pointer as `bunker://…`. */\nexport function toBunkerURL(pointer: BunkerPointer): string {\n const url = new URL(`bunker://${pointer.pubkey.toLowerCase()}`);\n for (const relay of pointer.relays) {\n url.searchParams.append(\"relay\", relay);\n }\n if (pointer.secret) {\n url.searchParams.set(\"secret\", pointer.secret);\n }\n return url.toString();\n}\n\n/**\n * Parse a `bunker://` URL into a pointer.\n * Returns null when the input is not a bunker URL (including NIP-05 identifiers).\n */\nexport function parseBunkerURL(input: string): BunkerPointer | null {\n const match = input.trim().match(BUNKER_REGEX);\n if (!match?.[1]) return null;\n try {\n const pubkey = assertHex32(match[1], \"bunker pubkey\");\n const qs = new URLSearchParams(match[2] ?? \"\");\n return {\n pubkey,\n relays: qs.getAll(\"relay\"),\n secret: qs.get(\"secret\"),\n };\n } catch {\n return null;\n }\n}\n\n/** Build a client-initiated `nostrconnect://` URI. */\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n if (!isHex32(params.clientPubkey)) {\n throw new Nip46Error(\"invalid client pubkey\");\n }\n if (!params.secret) {\n throw new Nip46Error(\"nostrconnect secret is required\");\n }\n if (params.relays.length === 0) {\n throw new Nip46Error(\"at least one relay is required\");\n }\n\n const query = new URLSearchParams();\n for (const relay of params.relays) {\n query.append(\"relay\", relay);\n }\n query.set(\"secret\", params.secret);\n if (params.perms && params.perms.length > 0) {\n query.set(\"perms\", params.perms.join(\",\"));\n }\n if (params.name) query.set(\"name\", params.name);\n if (params.url) query.set(\"url\", params.url);\n if (params.image) query.set(\"image\", params.image);\n\n return `nostrconnect://${params.clientPubkey.toLowerCase()}?${query.toString()}`;\n}\n\n/** Parse a `nostrconnect://` URI. */\nexport function parseNostrConnectURI(uri: string): NostrConnectParams {\n let url: URL;\n try {\n url = new URL(uri);\n } catch (cause) {\n throw new Nip46Error(`invalid nostrconnect URI: ${uri}`, {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n if (url.protocol !== \"nostrconnect:\") {\n throw new Nip46Error(`expected nostrconnect: scheme, got ${url.protocol}`);\n }\n const clientPubkey = url.hostname || url.pathname.replace(/^\\/*/, \"\");\n if (!isHex32(clientPubkey)) {\n throw new Nip46Error(\"invalid client pubkey in nostrconnect URI\");\n }\n const secret = url.searchParams.get(\"secret\");\n if (!secret) throw new Nip46Error(\"missing secret in nostrconnect URI\");\n const relays = url.searchParams.getAll(\"relay\");\n if (relays.length === 0) throw new Nip46Error(\"missing relays in nostrconnect URI\");\n const permsRaw = url.searchParams.get(\"perms\");\n return {\n clientPubkey: clientPubkey.toLowerCase(),\n relays,\n secret,\n perms: permsRaw ? permsRaw.split(\",\").filter(Boolean) : undefined,\n name: url.searchParams.get(\"name\") ?? undefined,\n url: url.searchParams.get(\"url\") ?? undefined,\n image: url.searchParams.get(\"image\") ?? undefined,\n };\n}\n\nexport function encodeNip46Request(req: Nip46Request): string {\n return JSON.stringify({ id: req.id, method: req.method, params: req.params });\n}\n\nexport function decodeNip46Request(json: string): Nip46Request {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch (cause) {\n throw new Nip46Error(\"invalid NIP-46 request JSON\", {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n if (!isRecord(data) || typeof data.id !== \"string\" || typeof data.method !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 request shape\");\n }\n if (!Array.isArray(data.params) || !data.params.every((p) => typeof p === \"string\")) {\n throw new Nip46Error(\"invalid NIP-46 request params\");\n }\n return { id: data.id, method: data.method, params: data.params as string[] };\n}\n\nexport function encodeNip46Response(res: Nip46Response): string {\n const body: Record<string, string> = { id: res.id };\n if (res.result !== undefined) body.result = res.result;\n if (res.error !== undefined) body.error = res.error;\n return JSON.stringify(body);\n}\n\nexport function decodeNip46Response(json: string): Nip46Response {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch (cause) {\n throw new Nip46Error(\"invalid NIP-46 response JSON\", {\n cause: cause instanceof Error ? cause : undefined,\n });\n }\n if (!isRecord(data) || typeof data.id !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response shape\");\n }\n if (data.result !== undefined && typeof data.result !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response result\");\n }\n if (data.error !== undefined && typeof data.error !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response error\");\n }\n return {\n id: data.id,\n result: data.result as string | undefined,\n error: data.error as string | undefined,\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],"mappings":";;;;;;;;;AASA,MAAa,eAAe;AAkC5B,IAAa,aAAb,cAAgC,WAAW;CACzC,YAAY,SAAiB,SAAwB;EACnD,MAAM,SAAS,OAAO;CACxB;AACF;;AAGA,SAAgB,YAAY,SAAgC;CAC1D,MAAM,MAAM,IAAI,IAAI,YAAY,QAAQ,OAAO,YAAY,GAAG;CAC9D,KAAK,MAAM,SAAS,QAAQ,QAC1B,IAAI,aAAa,OAAO,SAAS,KAAK;CAExC,IAAI,QAAQ,QACV,IAAI,aAAa,IAAI,UAAU,QAAQ,MAAM;CAE/C,OAAO,IAAI,SAAS;AACtB;;;;;AAMA,SAAgB,eAAe,OAAqC;CAClE,MAAM,QAAQ,MAAM,KAAK,CAAC,CAAC,MAAM,YAAY;CAC7C,IAAI,CAAC,QAAQ,IAAI,OAAO;CACxB,IAAI;EACF,MAAM,SAAS,YAAY,MAAM,IAAI,eAAe;EACpD,MAAM,KAAK,IAAI,gBAAgB,MAAM,MAAM,EAAE;EAC7C,OAAO;GACL;GACA,QAAQ,GAAG,OAAO,OAAO;GACzB,QAAQ,GAAG,IAAI,QAAQ;EACzB;CACF,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAgB,sBAAsB,QAAoC;CACxE,IAAI,CAAC,QAAQ,OAAO,YAAY,GAC9B,MAAM,IAAI,WAAW,uBAAuB;CAE9C,IAAI,CAAC,OAAO,QACV,MAAM,IAAI,WAAW,iCAAiC;CAExD,IAAI,OAAO,OAAO,WAAW,GAC3B,MAAM,IAAI,WAAW,gCAAgC;CAGvD,MAAM,QAAQ,IAAI,gBAAgB;CAClC,KAAK,MAAM,SAAS,OAAO,QACzB,MAAM,OAAO,SAAS,KAAK;CAE7B,MAAM,IAAI,UAAU,OAAO,MAAM;CACjC,IAAI,OAAO,SAAS,OAAO,MAAM,SAAS,GACxC,MAAM,IAAI,SAAS,OAAO,MAAM,KAAK,GAAG,CAAC;CAE3C,IAAI,OAAO,MAAM,MAAM,IAAI,QAAQ,OAAO,IAAI;CAC9C,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,OAAO,GAAG;CAC3C,IAAI,OAAO,OAAO,MAAM,IAAI,SAAS,OAAO,KAAK;CAEjD,OAAO,kBAAkB,OAAO,aAAa,YAAY,EAAE,GAAG,MAAM,SAAS;AAC/E;;AAGA,SAAgB,qBAAqB,KAAiC;CACpE,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,IAAI,GAAG;CACnB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,6BAA6B,OAAO,EACvD,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CACA,IAAI,IAAI,aAAa,iBACnB,MAAM,IAAI,WAAW,sCAAsC,IAAI,UAAU;CAE3E,MAAM,eAAe,IAAI,YAAY,IAAI,SAAS,QAAQ,QAAQ,EAAE;CACpE,IAAI,CAAC,QAAQ,YAAY,GACvB,MAAM,IAAI,WAAW,2CAA2C;CAElE,MAAM,SAAS,IAAI,aAAa,IAAI,QAAQ;CAC5C,IAAI,CAAC,QAAQ,MAAM,IAAI,WAAW,oCAAoC;CACtE,MAAM,SAAS,IAAI,aAAa,OAAO,OAAO;CAC9C,IAAI,OAAO,WAAW,GAAG,MAAM,IAAI,WAAW,oCAAoC;CAClF,MAAM,WAAW,IAAI,aAAa,IAAI,OAAO;CAC7C,OAAO;EACL,cAAc,aAAa,YAAY;EACvC;EACA;EACA,OAAO,WAAW,SAAS,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,IAAI,KAAA;EACxD,MAAM,IAAI,aAAa,IAAI,MAAM,KAAK,KAAA;EACtC,KAAK,IAAI,aAAa,IAAI,KAAK,KAAK,KAAA;EACpC,OAAO,IAAI,aAAa,IAAI,OAAO,KAAK,KAAA;CAC1C;AACF;AAEA,SAAgB,mBAAmB,KAA2B;CAC5D,OAAO,KAAK,UAAU;EAAE,IAAI,IAAI;EAAI,QAAQ,IAAI;EAAQ,QAAQ,IAAI;CAAO,CAAC;AAC9E;AAEA,SAAgB,mBAAmB,MAA4B;CAC7D,IAAI;CACJ,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,+BAA+B,EAClD,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CACA,IAAI,CAAC,SAAS,IAAI,KAAK,OAAO,KAAK,OAAO,YAAY,OAAO,KAAK,WAAW,UAC3E,MAAM,IAAI,WAAW,8BAA8B;CAErD,IAAI,CAAC,MAAM,QAAQ,KAAK,MAAM,KAAK,CAAC,KAAK,OAAO,OAAO,MAAM,OAAO,MAAM,QAAQ,GAChF,MAAM,IAAI,WAAW,+BAA+B;CAEtD,OAAO;EAAE,IAAI,KAAK;EAAI,QAAQ,KAAK;EAAQ,QAAQ,KAAK;CAAmB;AAC7E;AAEA,SAAgB,oBAAoB,KAA4B;CAC9D,MAAM,OAA+B,EAAE,IAAI,IAAI,GAAG;CAClD,IAAI,IAAI,WAAW,KAAA,GAAW,KAAK,SAAS,IAAI;CAChD,IAAI,IAAI,UAAU,KAAA,GAAW,KAAK,QAAQ,IAAI;CAC9C,OAAO,KAAK,UAAU,IAAI;AAC5B;AAEA,SAAgB,oBAAoB,MAA6B;CAC/D,IAAI;CACJ,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,gCAAgC,EACnD,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CACA,IAAI,CAAC,SAAS,IAAI,KAAK,OAAO,KAAK,OAAO,UACxC,MAAM,IAAI,WAAW,+BAA+B;CAEtD,IAAI,KAAK,WAAW,KAAA,KAAa,OAAO,KAAK,WAAW,UACtD,MAAM,IAAI,WAAW,gCAAgC;CAEvD,IAAI,KAAK,UAAU,KAAA,KAAa,OAAO,KAAK,UAAU,UACpD,MAAM,IAAI,WAAW,+BAA+B;CAEtD,OAAO;EACL,IAAI,KAAK;EACT,QAAQ,KAAK;EACb,OAAO,KAAK;CACd;AACF;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E"}
|
|
1
|
+
{"version":3,"file":"nip46.mjs","names":[],"sources":["../../src/nips/nip46.ts"],"sourcesContent":["import { NostrError } from \"../core/error.ts\";\n/**\n * NIP-46 (Nostr Connect) protocol helpers: bunker URI, nostrconnect URI, RPC JSON.\n * Transport/signing live in {@link Nip46Signer}.\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/46.md\n */\nimport { assertHex32, isHex32, isRecord } from \"../core/util.ts\";\n\n// oxlint-disable-next-line no-inferrable-types -- isolatedDeclarations requires the annotation for dts emit\nexport const BUNKER_REGEX: RegExp = /^bunker:\\/\\/([0-9a-fA-F]{64})\\??([?/\\w:.=&%-]*)$/;\n\nexport type BunkerPointer = {\n /** Remote signer / bunker public key (hex). */\n pubkey: string;\n relays: string[];\n secret: string | undefined;\n};\n\nexport type ClientMetadata = {\n name?: string | undefined;\n url?: string | undefined;\n image?: string | undefined;\n};\n\nexport type NostrConnectParams = {\n clientPubkey: string;\n relays: string[];\n secret: string;\n perms?: string[] | undefined;\n} & ClientMetadata;\n\nexport type Nip46Request = {\n id: string;\n method: string;\n params: string[];\n};\n\nexport type Nip46Response = {\n id: string;\n result?: string | undefined;\n error?: string | undefined;\n};\n\nexport class Nip46Error extends NostrError {\n override name = \"Nip46Error\";\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\n/** Encode a bunker pointer as `bunker://…`. */\nexport function toBunkerURL(pointer: BunkerPointer): string {\n const url = new URL(`bunker://${pointer.pubkey.toLowerCase()}`);\n for (const relay of pointer.relays) {\n url.searchParams.append(\"relay\", relay);\n }\n if (pointer.secret !== undefined && pointer.secret !== \"\") {\n url.searchParams.set(\"secret\", pointer.secret);\n }\n return url.toString();\n}\n\n/**\n * Parse a `bunker://` URL into a pointer. Returns undefined when the input is not a bunker URL\n * (including NIP-05 identifiers).\n */\nexport function parseBunkerURL(input: string): BunkerPointer | undefined {\n const match = BUNKER_REGEX.exec(input.trim());\n const hex = match?.at(1);\n if (match === null || hex === undefined) {\n return undefined;\n }\n try {\n const pubkey = assertHex32(hex, \"bunker pubkey\");\n const qs = new URLSearchParams(match.at(2) ?? \"\");\n return {\n pubkey,\n relays: qs.getAll(\"relay\"),\n secret: qs.get(\"secret\") ?? undefined,\n };\n } catch {\n return undefined;\n }\n}\n\n/** Build a client-initiated `nostrconnect://` URI. */\nexport function createNostrConnectURI(params: NostrConnectParams): string {\n if (!isHex32(params.clientPubkey.toLowerCase())) {\n throw new Nip46Error(\"invalid client pubkey\");\n }\n if (!params.secret) {\n throw new Nip46Error(\"nostrconnect secret is required\");\n }\n if (params.relays.length === 0) {\n throw new Nip46Error(\"at least one relay is required\");\n }\n\n const query = new URLSearchParams();\n for (const relay of params.relays) {\n query.append(\"relay\", relay);\n }\n query.set(\"secret\", params.secret);\n if (params.perms !== undefined && params.perms.length > 0) {\n query.set(\"perms\", params.perms.join(\",\"));\n }\n if (params.name !== undefined && params.name !== \"\") {\n query.set(\"name\", params.name);\n }\n if (params.url !== undefined && params.url !== \"\") {\n query.set(\"url\", params.url);\n }\n if (params.image !== undefined && params.image !== \"\") {\n query.set(\"image\", params.image);\n }\n\n return `nostrconnect://${params.clientPubkey.toLowerCase()}?${query.toString()}`;\n}\n\n/** Parse a `nostrconnect://` URI. */\nexport function parseNostrConnectURI(uri: string): NostrConnectParams {\n let url: URL;\n try {\n url = new URL(uri);\n } catch (error) {\n throw new Nip46Error(`invalid nostrconnect URI: ${uri}`, {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (url.protocol !== \"nostrconnect:\") {\n throw new Nip46Error(`expected nostrconnect: scheme, got ${url.protocol}`);\n }\n const clientPubkey = url.hostname || url.pathname.replace(/^\\/*/, \"\");\n if (!isHex32(clientPubkey.toLowerCase())) {\n throw new Nip46Error(\"invalid client pubkey in nostrconnect URI\");\n }\n const secret = url.searchParams.get(\"secret\");\n if (secret === null || secret === \"\") {\n throw new Nip46Error(\"missing secret in nostrconnect URI\");\n }\n const relays = url.searchParams.getAll(\"relay\");\n if (relays.length === 0) {\n throw new Nip46Error(\"missing relays in nostrconnect URI\");\n }\n const params: NostrConnectParams = {\n clientPubkey: clientPubkey.toLowerCase(),\n relays,\n secret,\n };\n const permsRaw = url.searchParams.get(\"perms\");\n if (permsRaw !== null && permsRaw !== \"\") {\n params.perms = permsRaw.split(\",\").filter((p) => p !== \"\");\n }\n const name = url.searchParams.get(\"name\");\n if (name !== null) {\n params.name = name;\n }\n const metadataUrl = url.searchParams.get(\"url\");\n if (metadataUrl !== null) {\n params.url = metadataUrl;\n }\n const image = url.searchParams.get(\"image\");\n if (image !== null) {\n params.image = image;\n }\n return params;\n}\n\nexport function encodeNip46Request(req: Nip46Request): string {\n return JSON.stringify({ id: req.id, method: req.method, params: req.params });\n}\n\nexport function decodeNip46Request(json: string): Nip46Request {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch (error) {\n throw new Nip46Error(\"invalid NIP-46 request JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (!isRecord(data) || typeof data[\"id\"] !== \"string\" || typeof data[\"method\"] !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 request shape\");\n }\n if (!Array.isArray(data[\"params\"]) || !data[\"params\"].every((p) => typeof p === \"string\")) {\n throw new Nip46Error(\"invalid NIP-46 request params\");\n }\n return { id: data[\"id\"], method: data[\"method\"], params: data[\"params\"] };\n}\n\nexport function encodeNip46Response(res: Nip46Response): string {\n const body: Record<string, string> = { id: res.id };\n if (res.result !== undefined) {\n body[\"result\"] = res.result;\n }\n if (res.error !== undefined) {\n body[\"error\"] = res.error;\n }\n return JSON.stringify(body);\n}\n\nexport function decodeNip46Response(json: string): Nip46Response {\n let data: unknown;\n try {\n data = JSON.parse(json);\n } catch (error) {\n throw new Nip46Error(\"invalid NIP-46 response JSON\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (!isRecord(data) || typeof data[\"id\"] !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response shape\");\n }\n // Some remote signers send explicit nulls for absent fields.\n const result = data[\"result\"] === null ? undefined : data[\"result\"];\n const error = data[\"error\"] === null ? undefined : data[\"error\"];\n if (result !== undefined && typeof result !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response result\");\n }\n if (error !== undefined && typeof error !== \"string\") {\n throw new Nip46Error(\"invalid NIP-46 response error\");\n }\n const response: Nip46Response = { id: data[\"id\"] };\n if (result !== undefined) {\n response.result = result;\n }\n if (error !== undefined) {\n response.error = error;\n }\n return response;\n}\n"],"mappings":";;;;;;;;AAUA,MAAa,eAAuB;AAkCpC,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;CAChB,YAAY,SAAiB,SAAwB;EACnD,MAAM,SAAS,OAAO;CACxB;AACF;;AAGA,SAAgB,YAAY,SAAgC;CAC1D,MAAM,MAAM,IAAI,IAAI,YAAY,QAAQ,OAAO,YAAY,GAAG;CAC9D,KAAK,MAAM,SAAS,QAAQ,QAC1B,IAAI,aAAa,OAAO,SAAS,KAAK;CAExC,IAAI,QAAQ,WAAW,KAAA,KAAa,QAAQ,WAAW,IACrD,IAAI,aAAa,IAAI,UAAU,QAAQ,MAAM;CAE/C,OAAO,IAAI,SAAS;AACtB;;;;;AAMA,SAAgB,eAAe,OAA0C;CACvE,MAAM,QAAQ,aAAa,KAAK,MAAM,KAAK,CAAC;CAC5C,MAAM,MAAM,OAAO,GAAG,CAAC;CACvB,IAAI,UAAU,QAAQ,QAAQ,KAAA,GAC5B;CAEF,IAAI;EACF,MAAM,SAAS,YAAY,KAAK,eAAe;EAC/C,MAAM,KAAK,IAAI,gBAAgB,MAAM,GAAG,CAAC,KAAK,EAAE;EAChD,OAAO;GACL;GACA,QAAQ,GAAG,OAAO,OAAO;GACzB,QAAQ,GAAG,IAAI,QAAQ,KAAK,KAAA;EAC9B;CACF,QAAQ;EACN;CACF;AACF;;AAGA,SAAgB,sBAAsB,QAAoC;CACxE,IAAI,CAAC,QAAQ,OAAO,aAAa,YAAY,CAAC,GAC5C,MAAM,IAAI,WAAW,uBAAuB;CAE9C,IAAI,CAAC,OAAO,QACV,MAAM,IAAI,WAAW,iCAAiC;CAExD,IAAI,OAAO,OAAO,WAAW,GAC3B,MAAM,IAAI,WAAW,gCAAgC;CAGvD,MAAM,QAAQ,IAAI,gBAAgB;CAClC,KAAK,MAAM,SAAS,OAAO,QACzB,MAAM,OAAO,SAAS,KAAK;CAE7B,MAAM,IAAI,UAAU,OAAO,MAAM;CACjC,IAAI,OAAO,UAAU,KAAA,KAAa,OAAO,MAAM,SAAS,GACtD,MAAM,IAAI,SAAS,OAAO,MAAM,KAAK,GAAG,CAAC;CAE3C,IAAI,OAAO,SAAS,KAAA,KAAa,OAAO,SAAS,IAC/C,MAAM,IAAI,QAAQ,OAAO,IAAI;CAE/B,IAAI,OAAO,QAAQ,KAAA,KAAa,OAAO,QAAQ,IAC7C,MAAM,IAAI,OAAO,OAAO,GAAG;CAE7B,IAAI,OAAO,UAAU,KAAA,KAAa,OAAO,UAAU,IACjD,MAAM,IAAI,SAAS,OAAO,KAAK;CAGjC,OAAO,kBAAkB,OAAO,aAAa,YAAY,EAAE,GAAG,MAAM,SAAS;AAC/E;;AAGA,SAAgB,qBAAqB,KAAiC;CACpE,IAAI;CACJ,IAAI;EACF,MAAM,IAAI,IAAI,GAAG;CACnB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,6BAA6B,OAAO,EACvD,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CACA,IAAI,IAAI,aAAa,iBACnB,MAAM,IAAI,WAAW,sCAAsC,IAAI,UAAU;CAE3E,MAAM,eAAe,IAAI,YAAY,IAAI,SAAS,QAAQ,QAAQ,EAAE;CACpE,IAAI,CAAC,QAAQ,aAAa,YAAY,CAAC,GACrC,MAAM,IAAI,WAAW,2CAA2C;CAElE,MAAM,SAAS,IAAI,aAAa,IAAI,QAAQ;CAC5C,IAAI,WAAW,QAAQ,WAAW,IAChC,MAAM,IAAI,WAAW,oCAAoC;CAE3D,MAAM,SAAS,IAAI,aAAa,OAAO,OAAO;CAC9C,IAAI,OAAO,WAAW,GACpB,MAAM,IAAI,WAAW,oCAAoC;CAE3D,MAAM,SAA6B;EACjC,cAAc,aAAa,YAAY;EACvC;EACA;CACF;CACA,MAAM,WAAW,IAAI,aAAa,IAAI,OAAO;CAC7C,IAAI,aAAa,QAAQ,aAAa,IACpC,OAAO,QAAQ,SAAS,MAAM,GAAG,CAAC,CAAC,QAAQ,MAAM,MAAM,EAAE;CAE3D,MAAM,OAAO,IAAI,aAAa,IAAI,MAAM;CACxC,IAAI,SAAS,MACX,OAAO,OAAO;CAEhB,MAAM,cAAc,IAAI,aAAa,IAAI,KAAK;CAC9C,IAAI,gBAAgB,MAClB,OAAO,MAAM;CAEf,MAAM,QAAQ,IAAI,aAAa,IAAI,OAAO;CAC1C,IAAI,UAAU,MACZ,OAAO,QAAQ;CAEjB,OAAO;AACT;AAEA,SAAgB,mBAAmB,KAA2B;CAC5D,OAAO,KAAK,UAAU;EAAE,IAAI,IAAI;EAAI,QAAQ,IAAI;EAAQ,QAAQ,IAAI;CAAO,CAAC;AAC9E;AAEA,SAAgB,mBAAmB,MAA4B;CAC7D,IAAI;CACJ,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,+BAA+B,EAClD,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CACA,IAAI,CAAC,SAAS,IAAI,KAAK,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,cAAc,UACjF,MAAM,IAAI,WAAW,8BAA8B;CAErD,IAAI,CAAC,MAAM,QAAQ,KAAK,SAAS,KAAK,CAAC,KAAK,SAAS,CAAC,OAAO,MAAM,OAAO,MAAM,QAAQ,GACtF,MAAM,IAAI,WAAW,+BAA+B;CAEtD,OAAO;EAAE,IAAI,KAAK;EAAO,QAAQ,KAAK;EAAW,QAAQ,KAAK;CAAU;AAC1E;AAEA,SAAgB,oBAAoB,KAA4B;CAC9D,MAAM,OAA+B,EAAE,IAAI,IAAI,GAAG;CAClD,IAAI,IAAI,WAAW,KAAA,GACjB,KAAK,YAAY,IAAI;CAEvB,IAAI,IAAI,UAAU,KAAA,GAChB,KAAK,WAAW,IAAI;CAEtB,OAAO,KAAK,UAAU,IAAI;AAC5B;AAEA,SAAgB,oBAAoB,MAA6B;CAC/D,IAAI;CACJ,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,gCAAgC,EACnD,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CACA,IAAI,CAAC,SAAS,IAAI,KAAK,OAAO,KAAK,UAAU,UAC3C,MAAM,IAAI,WAAW,+BAA+B;CAGtD,MAAM,SAAS,KAAK,cAAc,OAAO,KAAA,IAAY,KAAK;CAC1D,MAAM,QAAQ,KAAK,aAAa,OAAO,KAAA,IAAY,KAAK;CACxD,IAAI,WAAW,KAAA,KAAa,OAAO,WAAW,UAC5C,MAAM,IAAI,WAAW,gCAAgC;CAEvD,IAAI,UAAU,KAAA,KAAa,OAAO,UAAU,UAC1C,MAAM,IAAI,WAAW,+BAA+B;CAEtD,MAAM,WAA0B,EAAE,IAAI,KAAK,MAAM;CACjD,IAAI,WAAW,KAAA,GACb,SAAS,SAAS;CAEpB,IAAI,UAAU,KAAA,GACZ,SAAS,QAAQ;CAEnB,OAAO;AACT"}
|
package/dist/nips/nip49.d.mts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { a as NostrError } from "../error-
|
|
1
|
+
import { a as NostrError } from "../error-CR8Urc84.mjs";
|
|
2
2
|
//#region src/nips/nip49.d.ts
|
|
3
|
-
type Ncryptsec = `ncryptsec1${string}`;
|
|
4
|
-
type KeySecurityByte = 0x00 | 0x01 | 0x02;
|
|
5
|
-
declare class Nip49Error extends NostrError {
|
|
3
|
+
export type Ncryptsec = `ncryptsec1${string}`;
|
|
4
|
+
export type KeySecurityByte = 0x00 | 0x01 | 0x02;
|
|
5
|
+
export declare class Nip49Error extends NostrError {
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
6
8
|
/** Encrypt a 32-byte secret key to an `ncryptsec` bech32 string. */
|
|
7
|
-
declare function encrypt(secretKey: Uint8Array, password: string, logn?: number, ksb?: KeySecurityByte): Ncryptsec;
|
|
9
|
+
export declare function encrypt(secretKey: Uint8Array, password: string, logn?: number, ksb?: KeySecurityByte): Ncryptsec;
|
|
8
10
|
/** Decrypt an `ncryptsec` bech32 string to a 32-byte secret key. */
|
|
9
|
-
declare function decrypt(ncryptsec: string, password: string): Uint8Array;
|
|
11
|
+
export declare function decrypt(ncryptsec: string, password: string): Uint8Array;
|
|
10
12
|
//#endregion
|
|
11
|
-
export { KeySecurityByte, Ncryptsec, Nip49Error, decrypt, encrypt };
|
|
12
13
|
//# sourceMappingURL=nip49.d.mts.map
|
package/dist/nips/nip49.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as NostrError } from "../error-DZEs5OuZ.mjs";
|
|
1
|
+
import { g as NostrError, r as assertSecretKeyBytes } from "../util-LfQirgDk.mjs";
|
|
3
2
|
import { Bech32MaxSize, encodeBytes } from "./nip19.mjs";
|
|
4
3
|
import { concatBytes, randomBytes } from "@noble/hashes/utils.js";
|
|
5
4
|
import { bech32 } from "@scure/base";
|
|
@@ -19,7 +18,9 @@ const LOGN_MIN = 1;
|
|
|
19
18
|
const LOGN_MAX = 22;
|
|
20
19
|
const SCRYPT_R = 8;
|
|
21
20
|
const SCRYPT_P = 1;
|
|
22
|
-
var Nip49Error = class extends NostrError {
|
|
21
|
+
var Nip49Error = class extends NostrError {
|
|
22
|
+
name = "Nip49Error";
|
|
23
|
+
};
|
|
23
24
|
function assertLogn(logn) {
|
|
24
25
|
if (!Number.isInteger(logn) || logn < LOGN_MIN || logn > LOGN_MAX) throw new Nip49Error(`invalid logn ${logn}, expected integer ${LOGN_MIN}..${LOGN_MAX}`);
|
|
25
26
|
}
|
|
@@ -51,27 +52,30 @@ function decrypt(ncryptsec, password) {
|
|
|
51
52
|
let b;
|
|
52
53
|
try {
|
|
53
54
|
const decoded = bech32.decode(ncryptsec, Bech32MaxSize);
|
|
54
|
-
prefix = decoded
|
|
55
|
+
({prefix} = decoded);
|
|
55
56
|
b = new Uint8Array(bech32.fromWords(decoded.words));
|
|
56
|
-
} catch (
|
|
57
|
-
throw new Nip49Error("invalid ncryptsec", { cause:
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new Nip49Error("invalid ncryptsec", { cause: error instanceof Error ? error : void 0 });
|
|
58
59
|
}
|
|
59
60
|
if (prefix !== "ncryptsec") throw new Nip49Error(`invalid prefix ${prefix}, expected 'ncryptsec'`);
|
|
60
61
|
if (b.length !== PAYLOAD_LEN) throw new Nip49Error("invalid ncryptsec length");
|
|
61
|
-
const version = b
|
|
62
|
+
const version = b.at(0);
|
|
63
|
+
if (version === void 0) throw new Nip49Error("invalid ncryptsec length");
|
|
62
64
|
if (version !== VERSION) throw new Nip49Error(`invalid version ${version}, expected 0x02`);
|
|
63
|
-
const logn = b
|
|
65
|
+
const logn = b.at(1);
|
|
66
|
+
if (logn === void 0) throw new Nip49Error("invalid ncryptsec length");
|
|
64
67
|
const salt = b.subarray(2, 18);
|
|
65
68
|
const nonce = b.subarray(18, 42);
|
|
66
|
-
const ksb = b
|
|
69
|
+
const ksb = b.at(42);
|
|
70
|
+
if (ksb === void 0) throw new Nip49Error("invalid ncryptsec length");
|
|
67
71
|
const aad = Uint8Array.from([ksb]);
|
|
68
72
|
const ciphertext = b.subarray(43);
|
|
69
73
|
try {
|
|
70
74
|
const key = deriveKey(password, salt, logn);
|
|
71
75
|
return xchacha20poly1305(key, nonce, aad).decrypt(ciphertext);
|
|
72
|
-
} catch (
|
|
73
|
-
if (
|
|
74
|
-
throw new Nip49Error("failed to decrypt", { cause:
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error instanceof Nip49Error) throw error;
|
|
78
|
+
throw new Nip49Error("failed to decrypt", { cause: error instanceof Error ? error : void 0 });
|
|
75
79
|
}
|
|
76
80
|
}
|
|
77
81
|
//#endregion
|
package/dist/nips/nip49.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nip49.mjs","names":[],"sources":["../../src/nips/nip49.ts"],"sourcesContent":["/**\n * NIP-49: Private Key Encryption (`ncryptsec`).\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/49.md\n */\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\nimport { scrypt } from \"@noble/hashes/scrypt.js\";\nimport { concatBytes, randomBytes } from \"@noble/hashes/utils.js\";\nimport { bech32 } from \"@scure/base\";\nimport { NostrError } from \"../core/error.ts\";\nimport { assertSecretKeyBytes } from \"../core/util.ts\";\nimport { Bech32MaxSize, encodeBytes } from \"./nip19.ts\";\n\nexport type Ncryptsec = `ncryptsec1${string}`;\nexport type KeySecurityByte = 0x00 | 0x01 | 0x02;\n\nconst VERSION = 0x02;\nconst SALT_LEN = 16;\nconst NONCE_LEN = 24;\nconst PAYLOAD_LEN = 91;\nconst LOGN_MIN = 1;\nconst LOGN_MAX = 22;\nconst SCRYPT_R = 8;\nconst SCRYPT_P = 1;\n\nexport class Nip49Error extends NostrError {}\n\nfunction assertLogn(logn: number): void {\n if (!Number.isInteger(logn) || logn < LOGN_MIN || logn > LOGN_MAX) {\n throw new Nip49Error(`invalid logn ${logn}, expected integer ${LOGN_MIN}..${LOGN_MAX}`);\n }\n}\n\nfunction deriveKey(password: string, salt: Uint8Array, logn: number): Uint8Array {\n assertLogn(logn);\n const N = 2 ** logn;\n return scrypt(password.normalize(\"NFKC\"), salt, {\n N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n dkLen: 32,\n // noble 2.3: V (N blocks) + p B blocks + one tmp scratch block.\n maxmem: 128 * SCRYPT_R * (N + SCRYPT_P + 1),\n });\n}\n\n/** Encrypt a 32-byte secret key to an `ncryptsec` bech32 string. */\nexport function encrypt(\n secretKey: Uint8Array,\n password: string,\n logn
|
|
1
|
+
{"version":3,"file":"nip49.mjs","names":[],"sources":["../../src/nips/nip49.ts"],"sourcesContent":["/**\n * NIP-49: Private Key Encryption (`ncryptsec`).\n *\n * @see https://github.com/nostr-protocol/nips/blob/master/49.md\n */\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\nimport { scrypt } from \"@noble/hashes/scrypt.js\";\nimport { concatBytes, randomBytes } from \"@noble/hashes/utils.js\";\nimport { bech32 } from \"@scure/base\";\n\nimport { NostrError } from \"../core/error.ts\";\nimport { assertSecretKeyBytes } from \"../core/util.ts\";\nimport { Bech32MaxSize, encodeBytes } from \"./nip19.ts\";\n\nexport type Ncryptsec = `ncryptsec1${string}`;\nexport type KeySecurityByte = 0x00 | 0x01 | 0x02;\n\nconst VERSION = 0x02;\nconst SALT_LEN = 16;\nconst NONCE_LEN = 24;\nconst PAYLOAD_LEN = 91;\nconst LOGN_MIN = 1;\nconst LOGN_MAX = 22;\nconst SCRYPT_R = 8;\nconst SCRYPT_P = 1;\n\nexport class Nip49Error extends NostrError {\n override name = \"Nip49Error\";\n}\n\nfunction assertLogn(logn: number): void {\n if (!Number.isInteger(logn) || logn < LOGN_MIN || logn > LOGN_MAX) {\n throw new Nip49Error(`invalid logn ${logn}, expected integer ${LOGN_MIN}..${LOGN_MAX}`);\n }\n}\n\nfunction deriveKey(password: string, salt: Uint8Array, logn: number): Uint8Array {\n assertLogn(logn);\n const N = 2 ** logn;\n return scrypt(password.normalize(\"NFKC\"), salt, {\n N,\n r: SCRYPT_R,\n p: SCRYPT_P,\n dkLen: 32,\n // noble 2.3: V (N blocks) + p B blocks + one tmp scratch block.\n maxmem: 128 * SCRYPT_R * (N + SCRYPT_P + 1),\n });\n}\n\n/** Encrypt a 32-byte secret key to an `ncryptsec` bech32 string. */\nexport function encrypt(\n secretKey: Uint8Array,\n password: string,\n logn = 16,\n ksb: KeySecurityByte = 0x02,\n): Ncryptsec {\n assertSecretKeyBytes(secretKey);\n const salt = randomBytes(SALT_LEN);\n const key = deriveKey(password, salt, logn);\n const nonce = randomBytes(NONCE_LEN);\n const aad = Uint8Array.from([ksb]);\n const ciphertext = xchacha20poly1305(key, nonce, aad).encrypt(secretKey);\n const bytes = concatBytes(\n Uint8Array.from([VERSION]),\n Uint8Array.from([logn]),\n salt,\n nonce,\n aad,\n ciphertext,\n );\n return encodeBytes(\"ncryptsec\", bytes);\n}\n\n/** Decrypt an `ncryptsec` bech32 string to a 32-byte secret key. */\nexport function decrypt(ncryptsec: string, password: string): Uint8Array {\n let prefix: string;\n let b: Uint8Array;\n try {\n const decoded = bech32.decode(ncryptsec, Bech32MaxSize);\n ({ prefix } = decoded);\n b = new Uint8Array(bech32.fromWords(decoded.words));\n } catch (error) {\n throw new Nip49Error(\"invalid ncryptsec\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n if (prefix !== \"ncryptsec\") {\n throw new Nip49Error(`invalid prefix ${prefix}, expected 'ncryptsec'`);\n }\n if (b.length !== PAYLOAD_LEN) {\n throw new Nip49Error(\"invalid ncryptsec length\");\n }\n const version = b.at(0);\n if (version === undefined) {\n throw new Nip49Error(\"invalid ncryptsec length\");\n }\n if (version !== VERSION) {\n throw new Nip49Error(`invalid version ${version}, expected 0x02`);\n }\n const logn = b.at(1);\n if (logn === undefined) {\n throw new Nip49Error(\"invalid ncryptsec length\");\n }\n const salt = b.subarray(2, 2 + SALT_LEN);\n const nonce = b.subarray(2 + SALT_LEN, 2 + SALT_LEN + NONCE_LEN);\n const ksb = b.at(2 + SALT_LEN + NONCE_LEN);\n if (ksb === undefined) {\n throw new Nip49Error(\"invalid ncryptsec length\");\n }\n const aad = Uint8Array.from([ksb]);\n const ciphertext = b.subarray(2 + SALT_LEN + NONCE_LEN + 1);\n try {\n const key = deriveKey(password, salt, logn);\n return xchacha20poly1305(key, nonce, aad).decrypt(ciphertext);\n } catch (error) {\n if (error instanceof Nip49Error) {\n throw error;\n }\n throw new Nip49Error(\"failed to decrypt\", {\n cause: error instanceof Error ? error : undefined,\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAM,UAAU;AAChB,MAAM,WAAW;AACjB,MAAM,YAAY;AAClB,MAAM,cAAc;AACpB,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,WAAW;AACjB,MAAM,WAAW;AAEjB,IAAa,aAAb,cAAgC,WAAW;CACzC,OAAgB;AAClB;AAEA,SAAS,WAAW,MAAoB;CACtC,IAAI,CAAC,OAAO,UAAU,IAAI,KAAK,OAAO,YAAY,OAAO,UACvD,MAAM,IAAI,WAAW,gBAAgB,KAAK,qBAAqB,SAAS,IAAI,UAAU;AAE1F;AAEA,SAAS,UAAU,UAAkB,MAAkB,MAA0B;CAC/E,WAAW,IAAI;CACf,MAAM,IAAI,KAAK;CACf,OAAO,OAAO,SAAS,UAAU,MAAM,GAAG,MAAM;EAC9C;EACA,GAAG;EACH,GAAG;EACH,OAAO;EAEP,QAAQ,QAAkB,IAAI,WAAW;CAC3C,CAAC;AACH;;AAGA,SAAgB,QACd,WACA,UACA,OAAO,IACP,MAAuB,GACZ;CACX,qBAAqB,SAAS;CAC9B,MAAM,OAAO,YAAY,QAAQ;CACjC,MAAM,MAAM,UAAU,UAAU,MAAM,IAAI;CAC1C,MAAM,QAAQ,YAAY,SAAS;CACnC,MAAM,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;CACjC,MAAM,aAAa,kBAAkB,KAAK,OAAO,GAAG,CAAC,CAAC,QAAQ,SAAS;CACvE,MAAM,QAAQ,YACZ,WAAW,KAAK,CAAC,OAAO,CAAC,GACzB,WAAW,KAAK,CAAC,IAAI,CAAC,GACtB,MACA,OACA,KACA,UACF;CACA,OAAO,YAAY,aAAa,KAAK;AACvC;;AAGA,SAAgB,QAAQ,WAAmB,UAA8B;CACvE,IAAI;CACJ,IAAI;CACJ,IAAI;EACF,MAAM,UAAU,OAAO,OAAO,WAAW,aAAa;EACtD,CAAC,CAAE,UAAW;EACd,IAAI,IAAI,WAAW,OAAO,UAAU,QAAQ,KAAK,CAAC;CACpD,SAAS,OAAO;EACd,MAAM,IAAI,WAAW,qBAAqB,EACxC,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;CACA,IAAI,WAAW,aACb,MAAM,IAAI,WAAW,kBAAkB,OAAO,uBAAuB;CAEvE,IAAI,EAAE,WAAW,aACf,MAAM,IAAI,WAAW,0BAA0B;CAEjD,MAAM,UAAU,EAAE,GAAG,CAAC;CACtB,IAAI,YAAY,KAAA,GACd,MAAM,IAAI,WAAW,0BAA0B;CAEjD,IAAI,YAAY,SACd,MAAM,IAAI,WAAW,mBAAmB,QAAQ,gBAAgB;CAElE,MAAM,OAAO,EAAE,GAAG,CAAC;CACnB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,WAAW,0BAA0B;CAEjD,MAAM,OAAO,EAAE,SAAS,GAAG,EAAY;CACvC,MAAM,QAAQ,EAAE,SAAS,IAAc,EAAwB;CAC/D,MAAM,MAAM,EAAE,GAAG,EAAwB;CACzC,IAAI,QAAQ,KAAA,GACV,MAAM,IAAI,WAAW,0BAA0B;CAEjD,MAAM,MAAM,WAAW,KAAK,CAAC,GAAG,CAAC;CACjC,MAAM,aAAa,EAAE,SAAS,EAA4B;CAC1D,IAAI;EACF,MAAM,MAAM,UAAU,UAAU,MAAM,IAAI;EAC1C,OAAO,kBAAkB,KAAK,OAAO,GAAG,CAAC,CAAC,QAAQ,UAAU;CAC9D,SAAS,OAAO;EACd,IAAI,iBAAiB,YACnB,MAAM;EAER,MAAM,IAAI,WAAW,qBAAqB,EACxC,OAAO,iBAAiB,QAAQ,QAAQ,KAAA,EAC1C,CAAC;CACH;AACF"}
|