applesauce-signers 1.2.0 → 3.0.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/helpers/encryption.d.ts +1 -1
- package/dist/helpers/encryption.js +1 -6
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/nostr-connect.d.ts +92 -0
- package/dist/helpers/nostr-connect.js +93 -0
- package/dist/helpers/observable.d.ts +11 -0
- package/dist/helpers/observable.js +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/interface.d.ts +15 -0
- package/dist/signers/amber-clipboard-signer.d.ts +6 -6
- package/dist/signers/extension-signer.d.ts +9 -16
- package/dist/signers/extension-signer.js +9 -20
- package/dist/signers/index.d.ts +1 -0
- package/dist/signers/index.js +1 -0
- package/dist/signers/nostr-connect-provider.d.ts +128 -0
- package/dist/signers/nostr-connect-provider.js +406 -0
- package/dist/signers/nostr-connect-signer.d.ts +33 -93
- package/dist/signers/nostr-connect-signer.js +67 -75
- package/dist/signers/password-signer.d.ts +15 -6
- package/dist/signers/password-signer.js +31 -3
- package/dist/signers/readonly-signer.d.ts +14 -13
- package/dist/signers/readonly-signer.js +13 -9
- package/dist/signers/serial-port-signer.d.ts +4 -4
- package/dist/signers/simple-signer.d.ts +4 -1
- package/dist/signers/simple-signer.js +5 -0
- package/package.json +7 -4
- package/dist/__tests__/exports.test.js +0 -22
- package/dist/helpers/__tests__/exports.test.d.ts +0 -1
- package/dist/helpers/__tests__/exports.test.js +0 -11
- package/dist/nip-07.d.ts +0 -20
- package/dist/nip-07.js +0 -1
- package/dist/signers/__tests__/exports.test.d.ts +0 -1
- package/dist/signers/__tests__/exports.test.js +0 -21
- package/dist/signers/__tests__/nostr-connect-signer.test.d.ts +0 -1
- package/dist/signers/__tests__/nostr-connect-signer.test.js +0 -23
- /package/dist/{__tests__/exports.test.d.ts → interface.js} +0 -0
|
@@ -1,34 +1,11 @@
|
|
|
1
|
-
import { kinds, verifyEvent } from "nostr-tools";
|
|
2
|
-
import { SimpleSigner } from "applesauce-signers";
|
|
3
|
-
import { createDefer } from "applesauce-core/promise";
|
|
4
|
-
import { isHexKey, unixNow } from "applesauce-core/helpers";
|
|
5
1
|
import { logger } from "applesauce-core";
|
|
6
|
-
import {
|
|
2
|
+
import { getHiddenContent, unixNow } from "applesauce-core/helpers";
|
|
3
|
+
import { createDefer } from "applesauce-core/promise";
|
|
4
|
+
import { SimpleSigner } from "applesauce-signers";
|
|
7
5
|
import { nanoid } from "nanoid";
|
|
6
|
+
import { getPublicKey, kinds, verifyEvent } from "nostr-tools";
|
|
8
7
|
import { isNIP04 } from "../helpers/encryption.js";
|
|
9
|
-
|
|
10
|
-
return !!response.error;
|
|
11
|
-
}
|
|
12
|
-
export var Permission;
|
|
13
|
-
(function (Permission) {
|
|
14
|
-
Permission["GetPublicKey"] = "get_pubic_key";
|
|
15
|
-
Permission["SignEvent"] = "sign_event";
|
|
16
|
-
Permission["Nip04Encrypt"] = "nip04_encrypt";
|
|
17
|
-
Permission["Nip04Decrypt"] = "nip04_decrypt";
|
|
18
|
-
Permission["Nip44Encrypt"] = "nip44_encrypt";
|
|
19
|
-
Permission["Nip44Decrypt"] = "nip44_decrypt";
|
|
20
|
-
})(Permission || (Permission = {}));
|
|
21
|
-
export var NostrConnectMethod;
|
|
22
|
-
(function (NostrConnectMethod) {
|
|
23
|
-
NostrConnectMethod["Connect"] = "connect";
|
|
24
|
-
NostrConnectMethod["CreateAccount"] = "create_account";
|
|
25
|
-
NostrConnectMethod["GetPublicKey"] = "get_public_key";
|
|
26
|
-
NostrConnectMethod["SignEvent"] = "sign_event";
|
|
27
|
-
NostrConnectMethod["Nip04Encrypt"] = "nip04_encrypt";
|
|
28
|
-
NostrConnectMethod["Nip04Decrypt"] = "nip04_decrypt";
|
|
29
|
-
NostrConnectMethod["Nip44Encrypt"] = "nip44_encrypt";
|
|
30
|
-
NostrConnectMethod["Nip44Decrypt"] = "nip44_decrypt";
|
|
31
|
-
})(NostrConnectMethod || (NostrConnectMethod = {}));
|
|
8
|
+
import { NostrConnectMethod, buildSigningPermissions, createNostrConnectURI, parseBunkerURI, } from "../helpers/nostr-connect.js";
|
|
32
9
|
async function defaultHandleAuth(url) {
|
|
33
10
|
window.open(url, "auth", "width=400,height=600,resizable=no,status=no,location=no,toolbar=no,menubar=no");
|
|
34
11
|
}
|
|
@@ -37,10 +14,17 @@ export class NostrConnectSigner {
|
|
|
37
14
|
publishMethod;
|
|
38
15
|
/** The active nostr subscription */
|
|
39
16
|
subscriptionMethod;
|
|
17
|
+
/** A fallback method to use for subscriptionMethod if none is pass in when creating the signer */
|
|
18
|
+
static subscriptionMethod = undefined;
|
|
19
|
+
/** A fallback method to use for publishMethod if none is pass in when creating the signer */
|
|
20
|
+
static publishMethod = undefined;
|
|
21
|
+
/** A fallback pool to use if none is pass in when creating the signer */
|
|
22
|
+
static pool = undefined;
|
|
40
23
|
log = logger.extend("NostrConnectSigner");
|
|
41
24
|
/** The local client signer */
|
|
42
25
|
signer;
|
|
43
|
-
|
|
26
|
+
/** Whether the signer is listening for events */
|
|
27
|
+
listening = false;
|
|
44
28
|
/** Whether the signer is connected to the remote signer */
|
|
45
29
|
isConnected = false;
|
|
46
30
|
/** The users pubkey */
|
|
@@ -57,25 +41,27 @@ export class NostrConnectSigner {
|
|
|
57
41
|
onAuth = defaultHandleAuth;
|
|
58
42
|
verifyEvent = verifyEvent;
|
|
59
43
|
/** A secret used when initiating a connection from the client side */
|
|
60
|
-
|
|
44
|
+
secret;
|
|
61
45
|
nip04;
|
|
62
46
|
nip44;
|
|
63
|
-
/** A fallback method to use for subscriptionMethod if none is pass in when creating the signer */
|
|
64
|
-
static subscriptionMethod = undefined;
|
|
65
|
-
/** A fallback method to use for publishMethod if none is pass in when creating the signer */
|
|
66
|
-
static publishMethod = undefined;
|
|
67
47
|
constructor(opts) {
|
|
68
48
|
this.relays = opts.relays;
|
|
69
49
|
this.pubkey = opts.pubkey;
|
|
70
50
|
this.remote = opts.remote;
|
|
71
|
-
|
|
51
|
+
this.secret = opts.secret || nanoid(12);
|
|
52
|
+
// Get the subscription and publish methods
|
|
53
|
+
const subscriptionMethod = opts.subscriptionMethod ||
|
|
54
|
+
opts.pool?.subscription ||
|
|
55
|
+
NostrConnectSigner.subscriptionMethod ||
|
|
56
|
+
NostrConnectSigner.pool?.subscription;
|
|
72
57
|
if (!subscriptionMethod)
|
|
73
58
|
throw new Error("Missing subscriptionMethod, either pass a method or set NostrConnectSigner.subscriptionMethod");
|
|
74
|
-
const publishMethod = opts.publishMethod || NostrConnectSigner.publishMethod;
|
|
59
|
+
const publishMethod = opts.publishMethod || opts.pool?.publish || NostrConnectSigner.publishMethod || NostrConnectSigner.pool?.publish;
|
|
75
60
|
if (!publishMethod)
|
|
76
61
|
throw new Error("Missing publishMethod, either pass a method or set NostrConnectSigner.publishMethod");
|
|
77
|
-
this
|
|
78
|
-
this.
|
|
62
|
+
// Use arrow functions so "this" isn't bound to the signer
|
|
63
|
+
this.subscriptionMethod = (relays, filters) => subscriptionMethod(relays, filters);
|
|
64
|
+
this.publishMethod = (relays, event) => publishMethod(relays, event);
|
|
79
65
|
if (opts.onAuth)
|
|
80
66
|
this.onAuth = opts.onAuth;
|
|
81
67
|
this.signer = opts?.signer || new SimpleSigner();
|
|
@@ -92,9 +78,9 @@ export class NostrConnectSigner {
|
|
|
92
78
|
req;
|
|
93
79
|
/** Open the connection */
|
|
94
80
|
async open() {
|
|
95
|
-
if (this.
|
|
81
|
+
if (this.listening)
|
|
96
82
|
return;
|
|
97
|
-
this.
|
|
83
|
+
this.listening = true;
|
|
98
84
|
const pubkey = await this.signer.getPublicKey();
|
|
99
85
|
// Setup subscription
|
|
100
86
|
this.req = this.subscriptionMethod(this.relays, [
|
|
@@ -103,15 +89,24 @@ export class NostrConnectSigner {
|
|
|
103
89
|
"#p": [pubkey],
|
|
104
90
|
},
|
|
105
91
|
]).subscribe({
|
|
106
|
-
next: (event) => this.handleEvent(event),
|
|
92
|
+
next: (event) => typeof event !== "string" && this.handleEvent(event),
|
|
107
93
|
});
|
|
108
94
|
this.log("Opened", this.relays);
|
|
109
95
|
}
|
|
110
96
|
/** Close the connection */
|
|
111
97
|
async close() {
|
|
112
|
-
this.
|
|
98
|
+
this.listening = false;
|
|
113
99
|
this.isConnected = false;
|
|
114
|
-
|
|
100
|
+
// Close the current subscription
|
|
101
|
+
if (this.req) {
|
|
102
|
+
this.req.unsubscribe();
|
|
103
|
+
this.req = undefined;
|
|
104
|
+
}
|
|
105
|
+
// Cancel waiting promise
|
|
106
|
+
if (this.waitingPromise) {
|
|
107
|
+
this.waitingPromise.reject(new Error("Closed"));
|
|
108
|
+
this.waitingPromise = null;
|
|
109
|
+
}
|
|
115
110
|
this.log("Closed");
|
|
116
111
|
}
|
|
117
112
|
requests = new Map();
|
|
@@ -124,12 +119,15 @@ export class NostrConnectSigner {
|
|
|
124
119
|
if (this.remote && event.pubkey !== this.remote)
|
|
125
120
|
return;
|
|
126
121
|
try {
|
|
127
|
-
const responseStr =
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
const responseStr = getHiddenContent(event) ??
|
|
123
|
+
(isNIP04(event.content)
|
|
124
|
+
? await this.signer.nip04.decrypt(event.pubkey, event.content)
|
|
125
|
+
: await this.signer.nip44.decrypt(event.pubkey, event.content));
|
|
126
|
+
if (!responseStr)
|
|
127
|
+
return;
|
|
130
128
|
const response = JSON.parse(responseStr);
|
|
131
129
|
// handle remote signer connection
|
|
132
|
-
if (!this.remote && (response.result === "ack" || (this.
|
|
130
|
+
if (!this.remote && (response.result === "ack" || (this.secret && response.result === this.secret))) {
|
|
133
131
|
this.log("Got ack response from", event.pubkey, response.result);
|
|
134
132
|
this.isConnected = true;
|
|
135
133
|
this.remote = event.pubkey;
|
|
@@ -185,10 +183,16 @@ export class NostrConnectSigner {
|
|
|
185
183
|
const request = { id, method, params };
|
|
186
184
|
const encrypted = await this.signer.nip44.encrypt(this.remote, JSON.stringify(request));
|
|
187
185
|
const event = await this.createRequestEvent(encrypted, this.remote, kind);
|
|
188
|
-
this.log(`Sending
|
|
186
|
+
this.log(`Sending ${id} (${method}) ${JSON.stringify(params)}`);
|
|
189
187
|
const p = createDefer();
|
|
190
188
|
this.requests.set(id, p);
|
|
191
|
-
|
|
189
|
+
const result = this.publishMethod?.(this.relays, event);
|
|
190
|
+
// Handle returned Promise or Observable
|
|
191
|
+
if (result instanceof Promise)
|
|
192
|
+
await result;
|
|
193
|
+
else if ("subscribe" in result)
|
|
194
|
+
await new Promise((res) => result.subscribe({ complete: res }));
|
|
195
|
+
this.log(`Sent ${id} (${method})`);
|
|
192
196
|
return p;
|
|
193
197
|
}
|
|
194
198
|
/** Connect to remote signer */
|
|
@@ -216,11 +220,16 @@ export class NostrConnectSigner {
|
|
|
216
220
|
}
|
|
217
221
|
waitingPromise = null;
|
|
218
222
|
/** Wait for a remote signer to connect */
|
|
219
|
-
waitForSigner() {
|
|
223
|
+
waitForSigner(abort) {
|
|
220
224
|
if (this.isConnected)
|
|
221
225
|
return Promise.resolve();
|
|
222
226
|
this.open();
|
|
223
227
|
this.waitingPromise = createDefer();
|
|
228
|
+
abort?.addEventListener("abort", () => {
|
|
229
|
+
this.waitingPromise?.reject(new Error("Aborted"));
|
|
230
|
+
this.waitingPromise = null;
|
|
231
|
+
this.close();
|
|
232
|
+
}, true);
|
|
224
233
|
return this.waitingPromise;
|
|
225
234
|
}
|
|
226
235
|
/** Request to create an account on the remote signer */
|
|
@@ -295,37 +304,20 @@ export class NostrConnectSigner {
|
|
|
295
304
|
}
|
|
296
305
|
/** Returns the nostrconnect:// URI for this signer */
|
|
297
306
|
getNostrConnectURI(metadata) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (metadata?.image)
|
|
305
|
-
params.set("image", metadata.image);
|
|
306
|
-
if (metadata?.permissions)
|
|
307
|
-
params.set("perms", metadata.permissions.join(","));
|
|
308
|
-
for (const relay of this.relays)
|
|
309
|
-
params.append("relay", relay);
|
|
310
|
-
const client = getPublicKey(this.signer.key);
|
|
311
|
-
return `nostrconnect://${client}?` + params.toString();
|
|
307
|
+
return createNostrConnectURI({
|
|
308
|
+
client: getPublicKey(this.signer.key),
|
|
309
|
+
secret: this.secret,
|
|
310
|
+
relays: this.relays,
|
|
311
|
+
metadata,
|
|
312
|
+
});
|
|
312
313
|
}
|
|
313
314
|
/** Parses a bunker:// URI */
|
|
314
315
|
static parseBunkerURI(uri) {
|
|
315
|
-
|
|
316
|
-
// firefox puts pubkey part in host, chrome puts pubkey in pathname
|
|
317
|
-
const remote = url.host || url.pathname.replace("//", "");
|
|
318
|
-
if (!isHexKey(remote))
|
|
319
|
-
throw new Error("Invalid connection URI");
|
|
320
|
-
const relays = url.searchParams.getAll("relay");
|
|
321
|
-
if (relays.length === 0)
|
|
322
|
-
throw new Error("Missing relays");
|
|
323
|
-
const secret = url.searchParams.get("secret") ?? undefined;
|
|
324
|
-
return { remote, relays, secret };
|
|
316
|
+
return parseBunkerURI(uri);
|
|
325
317
|
}
|
|
326
318
|
/** Builds an array of signing permissions for event kinds */
|
|
327
319
|
static buildSigningPermissions(kinds) {
|
|
328
|
-
return
|
|
320
|
+
return buildSigningPermissions(kinds);
|
|
329
321
|
}
|
|
330
322
|
/** Create a {@link NostrConnectSigner} from a bunker:// URI */
|
|
331
323
|
static async fromBunkerURI(uri, options) {
|
|
@@ -1,29 +1,38 @@
|
|
|
1
1
|
import { EventTemplate } from "nostr-tools";
|
|
2
2
|
import { Deferred } from "applesauce-core/promise";
|
|
3
|
-
import {
|
|
3
|
+
import { ISigner } from "../interface.js";
|
|
4
4
|
/** A NIP-49 (Private Key Encryption) signer */
|
|
5
|
-
export declare class PasswordSigner implements
|
|
5
|
+
export declare class PasswordSigner implements ISigner {
|
|
6
6
|
key: Uint8Array | null;
|
|
7
7
|
ncryptsec?: string;
|
|
8
8
|
nip04: {
|
|
9
|
-
encrypt: (pubkey: string, plaintext: string) => Promise<string
|
|
10
|
-
decrypt: (pubkey: string, ciphertext: string) => Promise<string
|
|
9
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
|
10
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
|
11
11
|
};
|
|
12
12
|
nip44: {
|
|
13
|
-
encrypt: (pubkey: string, plaintext: string) => Promise<string
|
|
14
|
-
decrypt: (pubkey: string, ciphertext: string) => Promise<string
|
|
13
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
|
14
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
|
15
15
|
};
|
|
16
16
|
get unlocked(): boolean;
|
|
17
17
|
constructor();
|
|
18
18
|
unlockPromise?: Deferred<void>;
|
|
19
19
|
protected requestUnlock(): Deferred<void> | undefined;
|
|
20
|
+
/** Sets the ncryptsec from the key and password */
|
|
20
21
|
setPassword(password: string): Promise<void>;
|
|
22
|
+
/** Tests if the provided password is correct by decrypting the ncryptsec */
|
|
21
23
|
testPassword(password: string): Promise<void>;
|
|
24
|
+
/** Unlocks the signer by decrypting the ncryptsec using the provided password */
|
|
22
25
|
unlock(password: string): Promise<void>;
|
|
26
|
+
/** Locks the signer by removing the unencrypted key from memory */
|
|
27
|
+
lock(): void;
|
|
23
28
|
getPublicKey(): Promise<string>;
|
|
24
29
|
signEvent(event: EventTemplate): Promise<import("nostr-tools").VerifiedEvent>;
|
|
25
30
|
nip04Encrypt(pubkey: string, plaintext: string): Promise<string>;
|
|
26
31
|
nip04Decrypt(pubkey: string, ciphertext: string): Promise<string>;
|
|
27
32
|
nip44Encrypt(pubkey: string, plaintext: string): Promise<string>;
|
|
28
33
|
nip44Decrypt(pubkey: string, ciphertext: string): Promise<string>;
|
|
34
|
+
/** Creates a PasswordSigner from a hex private key or NIP-19 nsec and password */
|
|
35
|
+
static fromPrivateKey(privateKey: Uint8Array | string, password: string): Promise<PasswordSigner>;
|
|
36
|
+
/** Creates a PasswordSigner from a ncryptsec and unlocks it with the provided password */
|
|
37
|
+
static fromNcryptsec(ncryptsec: string, password?: string): Promise<PasswordSigner>;
|
|
29
38
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { finalizeEvent, getPublicKey, nip04, nip44 } from "nostr-tools";
|
|
2
2
|
import { encrypt, decrypt } from "nostr-tools/nip49";
|
|
3
3
|
import { createDefer } from "applesauce-core/promise";
|
|
4
|
+
import { normalizeToSecretKey } from "applesauce-core/helpers";
|
|
4
5
|
/** A NIP-49 (Private Key Encryption) signer */
|
|
5
6
|
export class PasswordSigner {
|
|
6
7
|
key = null;
|
|
@@ -30,11 +31,13 @@ export class PasswordSigner {
|
|
|
30
31
|
this.unlockPromise = p;
|
|
31
32
|
return p;
|
|
32
33
|
}
|
|
34
|
+
/** Sets the ncryptsec from the key and password */
|
|
33
35
|
async setPassword(password) {
|
|
34
36
|
if (!this.key)
|
|
35
37
|
throw new Error("Cant set password until unlocked");
|
|
36
38
|
this.ncryptsec = encrypt(this.key, password);
|
|
37
39
|
}
|
|
40
|
+
/** Tests if the provided password is correct by decrypting the ncryptsec */
|
|
38
41
|
async testPassword(password) {
|
|
39
42
|
if (this.ncryptsec) {
|
|
40
43
|
const key = decrypt(this.ncryptsec, password);
|
|
@@ -44,17 +47,27 @@ export class PasswordSigner {
|
|
|
44
47
|
else
|
|
45
48
|
throw new Error("Missing ncryptsec");
|
|
46
49
|
}
|
|
50
|
+
/** Unlocks the signer by decrypting the ncryptsec using the provided password */
|
|
47
51
|
async unlock(password) {
|
|
48
52
|
if (this.key)
|
|
49
53
|
return;
|
|
50
54
|
if (this.ncryptsec) {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
try {
|
|
56
|
+
this.key = decrypt(this.ncryptsec, password);
|
|
57
|
+
if (!this.key)
|
|
58
|
+
throw new Error("Failed to decrypt key");
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
throw new Error("failed to decrypt key: " + (error instanceof Error ? error.message : String(error)));
|
|
62
|
+
}
|
|
54
63
|
}
|
|
55
64
|
else
|
|
56
65
|
throw new Error("Missing ncryptsec");
|
|
57
66
|
}
|
|
67
|
+
/** Locks the signer by removing the unencrypted key from memory */
|
|
68
|
+
lock() {
|
|
69
|
+
this.key = null;
|
|
70
|
+
}
|
|
58
71
|
// public methods
|
|
59
72
|
async getPublicKey() {
|
|
60
73
|
await this.requestUnlock();
|
|
@@ -82,4 +95,19 @@ export class PasswordSigner {
|
|
|
82
95
|
await this.requestUnlock();
|
|
83
96
|
return nip44.v2.decrypt(ciphertext, nip44.v2.utils.getConversationKey(this.key, pubkey));
|
|
84
97
|
}
|
|
98
|
+
/** Creates a PasswordSigner from a hex private key or NIP-19 nsec and password */
|
|
99
|
+
static async fromPrivateKey(privateKey, password) {
|
|
100
|
+
const signer = new PasswordSigner();
|
|
101
|
+
signer.key = normalizeToSecretKey(privateKey);
|
|
102
|
+
await signer.setPassword(password);
|
|
103
|
+
return signer;
|
|
104
|
+
}
|
|
105
|
+
/** Creates a PasswordSigner from a ncryptsec and unlocks it with the provided password */
|
|
106
|
+
static async fromNcryptsec(ncryptsec, password) {
|
|
107
|
+
const signer = new PasswordSigner();
|
|
108
|
+
signer.ncryptsec = ncryptsec;
|
|
109
|
+
if (password)
|
|
110
|
+
await signer.unlock(password);
|
|
111
|
+
return signer;
|
|
112
|
+
}
|
|
85
113
|
}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import { VerifiedEvent } from "nostr-tools";
|
|
2
|
-
import {
|
|
2
|
+
import { ISigner } from "../interface.js";
|
|
3
3
|
/** A signer that only implements getPublicKey and throws on ever other method */
|
|
4
|
-
export declare class ReadonlySigner implements
|
|
4
|
+
export declare class ReadonlySigner implements ISigner {
|
|
5
5
|
private pubkey;
|
|
6
6
|
nip04: {
|
|
7
|
-
encrypt: (pubkey: string, plaintext: string) => Promise<string
|
|
8
|
-
decrypt: (pubkey: string, ciphertext: string) => Promise<string
|
|
7
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
|
8
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
|
9
9
|
};
|
|
10
10
|
nip44: {
|
|
11
|
-
encrypt: (pubkey: string, plaintext: string) => Promise<string
|
|
12
|
-
decrypt: (pubkey: string, ciphertext: string) => Promise<string
|
|
11
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
|
12
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
|
13
13
|
};
|
|
14
14
|
constructor(pubkey: string);
|
|
15
|
-
getPublicKey(): string
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
getPublicKey(): Promise<string>;
|
|
16
|
+
signEvent(): Promise<VerifiedEvent>;
|
|
17
|
+
nip04Encrypt(): Promise<string>;
|
|
18
|
+
nip04Decrypt(): Promise<string>;
|
|
19
|
+
nip44Encrypt(): Promise<string>;
|
|
20
|
+
nip44Decrypt(): Promise<string>;
|
|
21
|
+
/** Creates a ReadonlySigner from a hex public key or NIP-19 npub */
|
|
22
|
+
static fromPubkey(pubkey: string): ReadonlySigner;
|
|
22
23
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isHexKey, normalizeToPubkey } from "applesauce-core/helpers";
|
|
1
2
|
/** A signer that only implements getPublicKey and throws on ever other method */
|
|
2
3
|
export class ReadonlySigner {
|
|
3
4
|
pubkey;
|
|
@@ -5,6 +6,8 @@ export class ReadonlySigner {
|
|
|
5
6
|
nip44;
|
|
6
7
|
constructor(pubkey) {
|
|
7
8
|
this.pubkey = pubkey;
|
|
9
|
+
if (!isHexKey(pubkey))
|
|
10
|
+
throw new Error("Invalid public key");
|
|
8
11
|
this.nip04 = {
|
|
9
12
|
encrypt: this.nip04Encrypt.bind(this),
|
|
10
13
|
decrypt: this.nip04Decrypt.bind(this),
|
|
@@ -14,25 +17,26 @@ export class ReadonlySigner {
|
|
|
14
17
|
decrypt: this.nip44Decrypt.bind(this),
|
|
15
18
|
};
|
|
16
19
|
}
|
|
17
|
-
getPublicKey() {
|
|
20
|
+
async getPublicKey() {
|
|
18
21
|
return this.pubkey;
|
|
19
22
|
}
|
|
20
|
-
|
|
21
|
-
return {};
|
|
22
|
-
}
|
|
23
|
-
signEvent() {
|
|
23
|
+
async signEvent() {
|
|
24
24
|
throw new Error("Cant sign events with readonly");
|
|
25
25
|
}
|
|
26
|
-
nip04Encrypt() {
|
|
26
|
+
async nip04Encrypt() {
|
|
27
27
|
throw new Error("Cant encrypt with readonly");
|
|
28
28
|
}
|
|
29
|
-
nip04Decrypt() {
|
|
29
|
+
async nip04Decrypt() {
|
|
30
30
|
throw new Error("Cant decrypt with readonly");
|
|
31
31
|
}
|
|
32
|
-
nip44Encrypt() {
|
|
32
|
+
async nip44Encrypt() {
|
|
33
33
|
throw new Error("Cant encrypt with readonly");
|
|
34
34
|
}
|
|
35
|
-
nip44Decrypt() {
|
|
35
|
+
async nip44Decrypt() {
|
|
36
36
|
throw new Error("Cant decrypt with readonly");
|
|
37
37
|
}
|
|
38
|
+
/** Creates a ReadonlySigner from a hex public key or NIP-19 npub */
|
|
39
|
+
static fromPubkey(pubkey) {
|
|
40
|
+
return new ReadonlySigner(normalizeToPubkey(pubkey));
|
|
41
|
+
}
|
|
38
42
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Deferred } from "applesauce-core/promise";
|
|
2
2
|
import { EventTemplate, verifyEvent } from "nostr-tools";
|
|
3
|
-
import {
|
|
3
|
+
import { ISigner } from "../interface.js";
|
|
4
4
|
type Callback = () => void;
|
|
5
5
|
type DeviceOpts = {
|
|
6
6
|
onConnect?: Callback;
|
|
@@ -9,15 +9,15 @@ type DeviceOpts = {
|
|
|
9
9
|
onDone?: Callback;
|
|
10
10
|
};
|
|
11
11
|
/** A signer that works with [nostr-signing-device](https://github.com/lnbits/nostr-signing-device) */
|
|
12
|
-
export declare class SerialPortSigner implements
|
|
12
|
+
export declare class SerialPortSigner implements ISigner {
|
|
13
13
|
protected log: import("debug").Debugger;
|
|
14
14
|
protected writer: WritableStreamDefaultWriter<string> | null;
|
|
15
15
|
pubkey?: string;
|
|
16
16
|
get isConnected(): boolean;
|
|
17
17
|
verifyEvent: typeof verifyEvent;
|
|
18
18
|
nip04: {
|
|
19
|
-
encrypt: (pubkey: string, plaintext: string) => Promise<string
|
|
20
|
-
decrypt: (pubkey: string, ciphertext: string) => Promise<string
|
|
19
|
+
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
|
20
|
+
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
|
21
21
|
};
|
|
22
22
|
constructor();
|
|
23
23
|
protected lastCommand: Deferred<string> | null;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { EventTemplate } from "nostr-tools";
|
|
2
|
+
import { ISigner } from "../interface.js";
|
|
2
3
|
/** A Simple NIP-07 signer class */
|
|
3
|
-
export declare class SimpleSigner {
|
|
4
|
+
export declare class SimpleSigner implements ISigner {
|
|
4
5
|
key: Uint8Array;
|
|
5
6
|
constructor(key?: Uint8Array);
|
|
6
7
|
getPublicKey(): Promise<string>;
|
|
@@ -13,4 +14,6 @@ export declare class SimpleSigner {
|
|
|
13
14
|
encrypt: (pubkey: string, plaintext: string) => Promise<string>;
|
|
14
15
|
decrypt: (pubkey: string, ciphertext: string) => Promise<string>;
|
|
15
16
|
};
|
|
17
|
+
/** Creates a SimpleSigner from a hex private key or NIP-19 nsec */
|
|
18
|
+
static fromKey(privateKey: Uint8Array | string): SimpleSigner;
|
|
16
19
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeToSecretKey } from "applesauce-core/helpers";
|
|
1
2
|
import { finalizeEvent, generateSecretKey, getPublicKey, nip04, nip44 } from "nostr-tools";
|
|
2
3
|
/** A Simple NIP-07 signer class */
|
|
3
4
|
export class SimpleSigner {
|
|
@@ -19,4 +20,8 @@ export class SimpleSigner {
|
|
|
19
20
|
encrypt: async (pubkey, plaintext) => nip44.v2.encrypt(plaintext, nip44.v2.utils.getConversationKey(this.key, pubkey)),
|
|
20
21
|
decrypt: async (pubkey, ciphertext) => nip44.v2.decrypt(ciphertext, nip44.v2.utils.getConversationKey(this.key, pubkey)),
|
|
21
22
|
};
|
|
23
|
+
/** Creates a SimpleSigner from a hex private key or NIP-19 nsec */
|
|
24
|
+
static fromKey(privateKey) {
|
|
25
|
+
return new SimpleSigner(normalizeToSecretKey(privateKey));
|
|
26
|
+
}
|
|
22
27
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "applesauce-signers",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Signer classes for applesauce",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"keywords": [
|
|
9
9
|
"nostr",
|
|
10
|
+
"signer",
|
|
11
|
+
"nostr-connect",
|
|
10
12
|
"applesauce"
|
|
11
13
|
],
|
|
12
14
|
"author": "hzrd149",
|
|
@@ -36,16 +38,17 @@
|
|
|
36
38
|
"@noble/hashes": "^1.7.1",
|
|
37
39
|
"@noble/secp256k1": "^1.7.1",
|
|
38
40
|
"@scure/base": "^1.2.4",
|
|
39
|
-
"applesauce-core": "^
|
|
41
|
+
"applesauce-core": "^3.0.0",
|
|
40
42
|
"debug": "^4.4.0",
|
|
41
43
|
"nanoid": "^5.0.9",
|
|
42
|
-
"nostr-tools": "^2.
|
|
44
|
+
"nostr-tools": "^2.13"
|
|
43
45
|
},
|
|
44
46
|
"devDependencies": {
|
|
45
47
|
"@types/debug": "^4.1.12",
|
|
46
48
|
"@types/dom-serial": "^1.0.6",
|
|
47
49
|
"typescript": "^5.8.3",
|
|
48
|
-
"vitest": "^3.1.
|
|
50
|
+
"vitest": "^3.1.3",
|
|
51
|
+
"vitest-websocket-mock": "^0.5.0"
|
|
49
52
|
},
|
|
50
53
|
"funding": {
|
|
51
54
|
"type": "lightning",
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import * as exports from "../index.js";
|
|
3
|
-
describe("exports", () => {
|
|
4
|
-
it("should export the expected functions", () => {
|
|
5
|
-
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
-
[
|
|
7
|
-
"AmberClipboardSigner",
|
|
8
|
-
"ExtensionMissingError",
|
|
9
|
-
"ExtensionSigner",
|
|
10
|
-
"Helpers",
|
|
11
|
-
"NostrConnectMethod",
|
|
12
|
-
"NostrConnectSigner",
|
|
13
|
-
"PasswordSigner",
|
|
14
|
-
"Permission",
|
|
15
|
-
"ReadonlySigner",
|
|
16
|
-
"SerialPortSigner",
|
|
17
|
-
"SimpleSigner",
|
|
18
|
-
"isErrorResponse",
|
|
19
|
-
]
|
|
20
|
-
`);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import * as exports from "../index.js";
|
|
3
|
-
describe("exports", () => {
|
|
4
|
-
it("should export the expected functions", () => {
|
|
5
|
-
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
-
[
|
|
7
|
-
"isNIP04",
|
|
8
|
-
]
|
|
9
|
-
`);
|
|
10
|
-
});
|
|
11
|
-
});
|
package/dist/nip-07.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { EventTemplate, NostrEvent } from "nostr-tools";
|
|
2
|
-
export type Nip07Interface = {
|
|
3
|
-
getPublicKey: () => Promise<string> | string;
|
|
4
|
-
signEvent: (template: EventTemplate) => Promise<NostrEvent> | NostrEvent;
|
|
5
|
-
getRelays?: () => Record<string, {
|
|
6
|
-
read: boolean;
|
|
7
|
-
write: boolean;
|
|
8
|
-
}> | Promise<Record<string, {
|
|
9
|
-
read: boolean;
|
|
10
|
-
write: boolean;
|
|
11
|
-
}>>;
|
|
12
|
-
nip04?: {
|
|
13
|
-
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
|
14
|
-
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
|
15
|
-
};
|
|
16
|
-
nip44?: {
|
|
17
|
-
encrypt: (pubkey: string, plaintext: string) => Promise<string> | string;
|
|
18
|
-
decrypt: (pubkey: string, ciphertext: string) => Promise<string> | string;
|
|
19
|
-
};
|
|
20
|
-
};
|
package/dist/nip-07.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import * as exports from "../index.js";
|
|
3
|
-
describe("exports", () => {
|
|
4
|
-
it("should export the expected functions", () => {
|
|
5
|
-
expect(Object.keys(exports).sort()).toMatchInlineSnapshot(`
|
|
6
|
-
[
|
|
7
|
-
"AmberClipboardSigner",
|
|
8
|
-
"ExtensionMissingError",
|
|
9
|
-
"ExtensionSigner",
|
|
10
|
-
"NostrConnectMethod",
|
|
11
|
-
"NostrConnectSigner",
|
|
12
|
-
"PasswordSigner",
|
|
13
|
-
"Permission",
|
|
14
|
-
"ReadonlySigner",
|
|
15
|
-
"SerialPortSigner",
|
|
16
|
-
"SimpleSigner",
|
|
17
|
-
"isErrorResponse",
|
|
18
|
-
]
|
|
19
|
-
`);
|
|
20
|
-
});
|
|
21
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { NostrConnectSigner } from "../nostr-connect-signer.js";
|
|
3
|
-
import { SimpleSigner } from "../simple-signer.js";
|
|
4
|
-
describe("NostrConnectSigner", () => {
|
|
5
|
-
describe("connection", () => {
|
|
6
|
-
it("should call subscription method with filters", async () => {
|
|
7
|
-
const relays = ["wss://relay.signer.com"];
|
|
8
|
-
const subscription = vi.fn().mockReturnValue({ subscribe: vi.fn() });
|
|
9
|
-
const publish = vi.fn(async () => { });
|
|
10
|
-
const client = new SimpleSigner();
|
|
11
|
-
const remote = new SimpleSigner();
|
|
12
|
-
const signer = new NostrConnectSigner({
|
|
13
|
-
relays,
|
|
14
|
-
remote: await remote.getPublicKey(),
|
|
15
|
-
signer: client,
|
|
16
|
-
subscriptionMethod: subscription,
|
|
17
|
-
publishMethod: publish,
|
|
18
|
-
});
|
|
19
|
-
signer.connect();
|
|
20
|
-
expect(subscription).toHaveBeenCalledWith(relays, [{ "#p": [await client.getPublicKey()], kinds: [24133] }]);
|
|
21
|
-
});
|
|
22
|
-
});
|
|
23
|
-
});
|
|
File without changes
|