ns-auth-sdk 1.2.6 → 1.3.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.
@@ -0,0 +1,492 @@
1
+ import { seckeySigner } from "rx-nostr-crypto";
2
+
3
+ //#region src/utils/utils.ts
4
+ /**
5
+ * @packageDocumentation
6
+ */
7
+ /**
8
+ * @param bytes
9
+ * @returns
10
+ */
11
+ function bytesToHex(bytes) {
12
+ const key = "0123456789abcdef";
13
+ let hex = "";
14
+ for (let i = 0; i < bytes.length; i++) {
15
+ const firstNibble = bytes[i] >> 4;
16
+ const secondNibble = bytes[i] & 15;
17
+ hex += key[firstNibble] + key[secondNibble];
18
+ }
19
+ return hex;
20
+ }
21
+ /**
22
+ * @param hex
23
+ * @returns
24
+ */
25
+ function hexToBytes(hex) {
26
+ const key = "0123456789abcdef";
27
+ const bytes = [];
28
+ let currentByte = 0;
29
+ let highNibble = true;
30
+ for (let i = 0; i < hex.length; i++) {
31
+ const charValue = key.indexOf(hex[i].toLowerCase());
32
+ if (charValue === -1) continue;
33
+ if (highNibble) {
34
+ currentByte = charValue << 4;
35
+ highNibble = false;
36
+ } else {
37
+ currentByte += charValue;
38
+ bytes.push(currentByte);
39
+ highNibble = true;
40
+ }
41
+ }
42
+ return new Uint8Array(bytes);
43
+ }
44
+
45
+ //#endregion
46
+ //#region src/utils/key-cache.ts
47
+ /**
48
+ * Key cache manager for managing temporary secret keys
49
+ */
50
+ var KeyCache = class {
51
+ #cachedEntry = null;
52
+ #expiryTimer = null;
53
+ #cacheOptions = {
54
+ enabled: false,
55
+ timeoutMs: 300 * 1e3
56
+ };
57
+ /**
58
+ * KeyCache
59
+ * @param options
60
+ */
61
+ constructor(options) {
62
+ if (options) this.#cacheOptions = {
63
+ ...this.#cacheOptions,
64
+ ...options
65
+ };
66
+ }
67
+ /**
68
+ * @param options
69
+ */
70
+ setCacheOptions(options) {
71
+ if (Object.keys(options).length > 0 && this.#cachedEntry !== null) this.clearAllCachedKeys();
72
+ this.#cacheOptions = {
73
+ ...this.#cacheOptions,
74
+ ...options
75
+ };
76
+ }
77
+ getCacheOptions() {
78
+ return { ...this.#cacheOptions };
79
+ }
80
+ isEnabled() {
81
+ return this.#cacheOptions.enabled;
82
+ }
83
+ /**
84
+ * @param credentialId
85
+ * @param sk
86
+ */
87
+ setKey(credentialId, sk) {
88
+ if (!this.#cacheOptions.enabled) return;
89
+ const id = typeof credentialId === "string" ? credentialId : bytesToHex(credentialId);
90
+ const timeout = this.#cacheOptions.timeoutMs !== void 0 ? this.#cacheOptions.timeoutMs : 300 * 1e3;
91
+ const expireAt = Date.now() + timeout;
92
+ this.#clearCachedEntry();
93
+ this.#cachedEntry = {
94
+ id,
95
+ sk: new Uint8Array(sk),
96
+ expireAt
97
+ };
98
+ try {
99
+ this.#scheduleExpiry();
100
+ } catch (error) {
101
+ this.#clearCachedEntry();
102
+ throw error;
103
+ }
104
+ }
105
+ /**
106
+ * @param credentialId
107
+ * @returns undefined
108
+ */
109
+ getKey(credentialId) {
110
+ if (!this.#cacheOptions.enabled) return void 0;
111
+ const id = typeof credentialId === "string" ? credentialId : bytesToHex(credentialId);
112
+ return this.#getCachedKeyIfValid(id);
113
+ }
114
+ /**
115
+ * @param credentialId
116
+ */
117
+ clearCachedKey(credentialId) {
118
+ const id = typeof credentialId === "string" ? credentialId : bytesToHex(credentialId);
119
+ if (this.#cachedEntry && this.#cachedEntry.id === id) this.#clearCachedEntry();
120
+ }
121
+ clearAllCachedKeys() {
122
+ this.#clearCachedEntry();
123
+ }
124
+ /**
125
+ * @param credentialId
126
+ * @returns undefined
127
+ */
128
+ #getCachedKeyIfValid(credentialId) {
129
+ if (!this.#cachedEntry || this.#cachedEntry.id !== credentialId) return;
130
+ if (Date.now() < this.#cachedEntry.expireAt) return this.#cachedEntry.sk;
131
+ this.#clearCachedEntry();
132
+ }
133
+ #clearCachedEntry() {
134
+ if (this.#cachedEntry) {
135
+ this.#clearKey(this.#cachedEntry.sk);
136
+ this.#cachedEntry = null;
137
+ }
138
+ if (this.#expiryTimer) {
139
+ clearTimeout(this.#expiryTimer);
140
+ this.#expiryTimer = null;
141
+ }
142
+ }
143
+ #scheduleExpiry() {
144
+ if (!this.#cachedEntry) return;
145
+ const now = Date.now();
146
+ const timeToExpiry = this.#cachedEntry.expireAt - now;
147
+ if (timeToExpiry <= 0) {
148
+ this.#clearCachedEntry();
149
+ return;
150
+ }
151
+ this.#expiryTimer = setTimeout(() => {
152
+ this.#clearCachedEntry();
153
+ }, timeToExpiry + 1);
154
+ }
155
+ /**
156
+ * @param key
157
+ */
158
+ #clearKey(key) {
159
+ key?.fill?.(0);
160
+ }
161
+ };
162
+
163
+ //#endregion
164
+ //#region src/utils/prf-handler.ts
165
+ const PRF_EVAL_INPUT = new TextEncoder().encode("nostr-pwk");
166
+ /**
167
+ * @returns PRF
168
+ */
169
+ async function isPrfSupported() {
170
+ try {
171
+ const response = await navigator.credentials.get({ publicKey: {
172
+ challenge: crypto.getRandomValues(new Uint8Array(32)),
173
+ allowCredentials: [],
174
+ userVerification: "required",
175
+ extensions: { prf: { eval: { first: PRF_EVAL_INPUT } } }
176
+ } });
177
+ if (!response) return false;
178
+ return !!response.getClientExtensionResults()?.prf?.results?.first;
179
+ } catch {
180
+ return false;
181
+ }
182
+ }
183
+ /**
184
+ * @param options
185
+ * @returns Credential
186
+ */
187
+ async function createPasskey(options = {}) {
188
+ const rpName = options.rp?.name || (typeof location !== "undefined" ? location.host : "Nosskey");
189
+ const rpId = options.rp?.id;
190
+ const userName = options.user?.name || "user@example.com";
191
+ const userDisplayName = options.user?.displayName || "Nosskey user";
192
+ const credentialCreationOptions = { publicKey: {
193
+ rp: {
194
+ name: rpName,
195
+ id: rpId
196
+ },
197
+ user: {
198
+ id: crypto.getRandomValues(new Uint8Array(16)),
199
+ name: userName,
200
+ displayName: userDisplayName
201
+ },
202
+ pubKeyCredParams: options.pubKeyCredParams || [{
203
+ type: "public-key",
204
+ alg: -7
205
+ }],
206
+ authenticatorSelection: options.authenticatorSelection || {
207
+ residentKey: "required",
208
+ userVerification: "required"
209
+ },
210
+ challenge: crypto.getRandomValues(new Uint8Array(32)),
211
+ extensions: options.extensions || { prf: {} }
212
+ } };
213
+ const cred = await navigator.credentials.create(credentialCreationOptions);
214
+ return new Uint8Array(cred.rawId);
215
+ }
216
+ /**
217
+ * ID
218
+ * @param credentialId
219
+ * @param options PRF(rpId、timeout、userVerification)
220
+ * @returns PRF credentialID
221
+ */
222
+ async function getPrfSecret(credentialId, options) {
223
+ const allowCredentials = credentialId ? [{
224
+ type: "public-key",
225
+ id: credentialId
226
+ }] : [];
227
+ const requestOptions = {
228
+ challenge: crypto.getRandomValues(new Uint8Array(32)),
229
+ allowCredentials,
230
+ userVerification: options?.userVerification || "required",
231
+ extensions: { prf: { eval: { first: PRF_EVAL_INPUT } } }
232
+ };
233
+ if (options?.rpId) requestOptions.rpId = options.rpId;
234
+ if (options?.timeout) requestOptions.timeout = options.timeout;
235
+ const response = await navigator.credentials.get({ publicKey: requestOptions });
236
+ if (!response) throw new Error("Authentication failed");
237
+ const secret = response.getClientExtensionResults()?.prf?.results?.first;
238
+ if (!secret) throw new Error("PRF secret not available");
239
+ const responseId = new Uint8Array(response.rawId);
240
+ return {
241
+ secret: new Uint8Array(secret),
242
+ id: responseId
243
+ };
244
+ }
245
+
246
+ //#endregion
247
+ //#region src/utils/nosskey.ts
248
+ /**
249
+ * Nosskey class for Passkey-Derived Nostr Identity
250
+ * @packageDocumentation
251
+ */
252
+ const STANDARD_SALT = "6e6f7374722d6b6579";
253
+ /**
254
+ * Nosskey - Passkey-Derived Nostr Keys
255
+ */
256
+ var NosskeyManager = class {
257
+ #keyCache;
258
+ #currentKeyInfo = null;
259
+ #storageOptions = {
260
+ enabled: true,
261
+ storageKey: "nosskey_keyinfo"
262
+ };
263
+ #prfOptions = {};
264
+ /**
265
+ * NosskeyManager
266
+ * @param options
267
+ */
268
+ constructor(options) {
269
+ this.#keyCache = new KeyCache(options?.cacheOptions);
270
+ if (options?.storageOptions) this.#storageOptions = {
271
+ ...this.#storageOptions,
272
+ ...options.storageOptions
273
+ };
274
+ const userVerification = options?.prfOptions?.userVerification ?? "required";
275
+ if (options?.prfOptions) this.#prfOptions = {
276
+ ...options.prfOptions,
277
+ userVerification
278
+ };
279
+ else this.#prfOptions = { userVerification };
280
+ if (this.#storageOptions.enabled) {
281
+ const loadedKeyInfo = this.#loadKeyInfoFromStorage();
282
+ if (loadedKeyInfo) this.#currentKeyInfo = loadedKeyInfo;
283
+ }
284
+ }
285
+ /**
286
+ * NostrKeyInfo
287
+ * @param options
288
+ */
289
+ setStorageOptions(options) {
290
+ this.#storageOptions = {
291
+ ...this.#storageOptions,
292
+ ...options
293
+ };
294
+ if (options.enabled === false) this.clearStoredKeyInfo();
295
+ }
296
+ /**
297
+ * NostrKeyInfo
298
+ */
299
+ getStorageOptions() {
300
+ return { ...this.#storageOptions };
301
+ }
302
+ /**
303
+ * NostrKeyInfo
304
+ * @param keyInfo NostrKeyInfo
305
+ */
306
+ setCurrentKeyInfo(keyInfo) {
307
+ this.#currentKeyInfo = keyInfo;
308
+ if (this.#storageOptions.enabled) this.#saveKeyInfoToStorage(keyInfo);
309
+ }
310
+ /**
311
+ * NostrKeyInfo
312
+ */
313
+ getCurrentKeyInfo() {
314
+ if (!this.#currentKeyInfo && this.#storageOptions.enabled) this.#currentKeyInfo = this.#loadKeyInfoFromStorage();
315
+ return this.#currentKeyInfo;
316
+ }
317
+ /**
318
+ * NostrKeyInfo
319
+ * @returns NostrKeyInfo
320
+ */
321
+ hasKeyInfo() {
322
+ if (this.#currentKeyInfo) return true;
323
+ if (this.#storageOptions.enabled) {
324
+ const loadedKeyInfo = this.#loadKeyInfoFromStorage();
325
+ if (loadedKeyInfo) {
326
+ this.#currentKeyInfo = loadedKeyInfo;
327
+ return true;
328
+ }
329
+ }
330
+ return false;
331
+ }
332
+ /**
333
+ * NostrKeyInfo
334
+ * @param keyInfo NostrKeyInfo
335
+ */
336
+ async #saveKeyInfoToStorage(keyInfo) {
337
+ if (!this.#storageOptions.enabled) return;
338
+ const storage = this.#storageOptions.storage || (typeof localStorage !== "undefined" ? localStorage : null);
339
+ if (!storage) return;
340
+ const key = this.#storageOptions.storageKey || "nosskey_keyinfo";
341
+ storage.setItem(key, JSON.stringify(keyInfo));
342
+ }
343
+ /**
344
+ * NostrKeyInfo
345
+ */
346
+ #loadKeyInfoFromStorage() {
347
+ if (!this.#storageOptions.enabled) return null;
348
+ const storage = this.#storageOptions.storage || (typeof localStorage !== "undefined" ? localStorage : null);
349
+ if (!storage) return null;
350
+ const key = this.#storageOptions.storageKey || "nosskey_keyinfo";
351
+ const data = storage.getItem(key);
352
+ if (!data) return null;
353
+ try {
354
+ return JSON.parse(data);
355
+ } catch (e) {
356
+ console.error("Failed to parse stored NostrKeyInfo", e);
357
+ return null;
358
+ }
359
+ }
360
+ /**
361
+ * NostrKeyInfo
362
+ */
363
+ clearStoredKeyInfo() {
364
+ const storage = this.#storageOptions.storage || (typeof localStorage !== "undefined" ? localStorage : null);
365
+ if (!storage) return;
366
+ const key = this.#storageOptions.storageKey || "nosskey_keyinfo";
367
+ storage.removeItem(key);
368
+ this.#currentKeyInfo = null;
369
+ }
370
+ /**
371
+ * NIP-07
372
+ * NostrKeyInfo
373
+ */
374
+ async getPublicKey() {
375
+ const keyInfo = this.getCurrentKeyInfo();
376
+ if (!keyInfo) throw new Error("No current NostrKeyInfo set");
377
+ return keyInfo.pubkey;
378
+ }
379
+ /**
380
+ * NIP-07
381
+ * NostrKeyInfo
382
+ * @param event Nostr
383
+ */
384
+ async signEvent(event) {
385
+ const keyInfo = this.getCurrentKeyInfo();
386
+ if (!keyInfo) throw new Error("No current NostrKeyInfo set");
387
+ return this.signEventWithKeyInfo(event, keyInfo);
388
+ }
389
+ /**
390
+ * @param options
391
+ */
392
+ setCacheOptions(options) {
393
+ this.#keyCache.setCacheOptions(options);
394
+ }
395
+ getCacheOptions() {
396
+ return this.#keyCache.getCacheOptions();
397
+ }
398
+ /**
399
+ * @param credentialId
400
+ */
401
+ clearCachedKey(credentialId) {
402
+ this.#keyCache.clearCachedKey(credentialId);
403
+ }
404
+ clearAllCachedKeys() {
405
+ this.#keyCache.clearAllCachedKeys();
406
+ }
407
+ /**
408
+ * @param options
409
+ * @returns Credential
410
+ */
411
+ async createPasskey(options = {}) {
412
+ return createPasskey({
413
+ rp: {
414
+ id: this.#prfOptions.rpId,
415
+ name: this.#prfOptions.rpId
416
+ },
417
+ authenticatorSelection: { userVerification: this.#prfOptions.userVerification },
418
+ ...options
419
+ });
420
+ }
421
+ /**
422
+ * PRF NostrKeyInfo
423
+ * @param credentialId
424
+ * @param options
425
+ */
426
+ async createNostrKey(credentialId, options = {}) {
427
+ const { secret: sk, id: responseId } = await getPrfSecret(credentialId, this.#prfOptions);
428
+ if (sk.every((byte) => byte === 0)) throw new Error("Invalid PRF output: all zeros");
429
+ const publicKey = await seckeySigner(bytesToHex(sk)).getPublicKey();
430
+ return {
431
+ credentialId: bytesToHex(credentialId || responseId),
432
+ pubkey: publicKey,
433
+ salt: STANDARD_SALT,
434
+ ...options.username && { username: options.username }
435
+ };
436
+ }
437
+ /**
438
+ * @param event Nostr
439
+ * @param keyInfo NostrKeyInfo
440
+ * @param options
441
+ */
442
+ async signEventWithKeyInfo(event, keyInfo, options = {}) {
443
+ const { clearMemory = true, tags } = options;
444
+ const shouldUseCache = this.#keyCache.isEnabled();
445
+ let sk;
446
+ if (shouldUseCache) sk = this.#keyCache.getKey(keyInfo.credentialId);
447
+ if (!sk) {
448
+ const { secret: prfSecret } = await getPrfSecret(hexToBytes(keyInfo.credentialId), this.#prfOptions);
449
+ sk = prfSecret;
450
+ if (shouldUseCache) this.#keyCache.setKey(keyInfo.credentialId, sk);
451
+ }
452
+ const signedEvent = await seckeySigner(bytesToHex(sk), { tags }).signEvent(event);
453
+ if (!shouldUseCache && clearMemory) this.#clearKey(sk);
454
+ return signedEvent;
455
+ }
456
+ /**
457
+ * @param keyInfo NostrKeyInfo
458
+ * @param credentialId NostrKeyInfoのcredentialId
459
+ * @param options
460
+ * @returns
461
+ */
462
+ async exportNostrKey(keyInfo, credentialId) {
463
+ let usedCredentialId = credentialId;
464
+ if (!usedCredentialId && keyInfo.credentialId) usedCredentialId = hexToBytes(keyInfo.credentialId);
465
+ const { secret: sk } = await getPrfSecret(usedCredentialId, this.#prfOptions);
466
+ return bytesToHex(sk);
467
+ }
468
+ /**
469
+ * PRF
470
+ */
471
+ async isPrfSupported() {
472
+ return isPrfSupported();
473
+ }
474
+ /**
475
+ * @param key
476
+ */
477
+ #clearKey(key) {
478
+ key?.fill?.(0);
479
+ }
480
+ };
481
+
482
+ //#endregion
483
+ //#region src/utils/crypto-utils.ts
484
+ /**
485
+ * Cryptographic utilities for Nosskey
486
+ * @packageDocumentation
487
+ */
488
+ const INFO_BYTES = new TextEncoder().encode("nostr-pwk");
489
+
490
+ //#endregion
491
+ export { bytesToHex as a, isPrfSupported as i, createPasskey as n, hexToBytes as o, getPrfSecret as r, NosskeyManager as t };
492
+ //# sourceMappingURL=utils-CV_lg5Ir.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-CV_lg5Ir.mjs","names":["#cacheOptions","#cachedEntry","#clearCachedEntry","#scheduleExpiry","#getCachedKeyIfValid","#clearKey","#expiryTimer","#keyCache","#storageOptions","#prfOptions","#loadKeyInfoFromStorage","#currentKeyInfo","#saveKeyInfoToStorage","#clearKey"],"sources":["../src/utils/utils.ts","../src/utils/key-cache.ts","../src/utils/prf-handler.ts","../src/utils/nosskey.ts","../src/utils/crypto-utils.ts"],"sourcesContent":["/**\n * @packageDocumentation\n */\n\n/**\n * @param bytes\n * @returns\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n const key = '0123456789abcdef';\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n const firstNibble = bytes[i] >> 4;\n const secondNibble = bytes[i] & 15;\n hex += key[firstNibble] + key[secondNibble];\n }\n return hex;\n}\n\n/**\n * @param hex\n * @returns\n */\nexport function hexToBytes(hex: string): Uint8Array {\n const key = '0123456789abcdef';\n const bytes = [];\n let currentByte = 0;\n let highNibble = true;\n\n for (let i = 0; i < hex.length; i++) {\n const charValue = key.indexOf(hex[i].toLowerCase());\n if (charValue === -1) continue;\n\n if (highNibble) {\n currentByte = charValue << 4;\n highNibble = false;\n } else {\n currentByte += charValue;\n bytes.push(currentByte);\n highNibble = true;\n }\n }\n\n return new Uint8Array(bytes);\n}\n","/**\n * Key cache management for Nosskey\n * @packageDocumentation\n */\n\nimport type { KeyCacheOptions } from './types.js';\nimport { bytesToHex } from './utils.js';\n\n/**\n * Key cache entry with expiration time\n */\ninterface CacheEntry {\n id: string;\n sk: Uint8Array;\n expireAt: number;\n}\n\n/**\n * Key cache manager for managing temporary secret keys\n */\nexport class KeyCache {\n #cachedEntry: CacheEntry | null = null;\n\n #expiryTimer: NodeJS.Timeout | null = null;\n\n #cacheOptions: KeyCacheOptions = {\n enabled: false,\n timeoutMs: 5 * 60 * 1000,\n };\n\n /**\n * KeyCache\n * @param options\n */\n constructor(options?: Partial<KeyCacheOptions>) {\n if (options) {\n this.#cacheOptions = { ...this.#cacheOptions, ...options };\n }\n }\n\n /**\n * @param options\n */\n setCacheOptions(options: Partial<KeyCacheOptions>): void {\n if (Object.keys(options).length > 0 && this.#cachedEntry !== null) {\n this.clearAllCachedKeys();\n }\n\n this.#cacheOptions = { ...this.#cacheOptions, ...options };\n }\n\n getCacheOptions(): KeyCacheOptions {\n return { ...this.#cacheOptions };\n }\n\n isEnabled(): boolean {\n return this.#cacheOptions.enabled;\n }\n\n /**\n * @param credentialId\n * @param sk\n */\n setKey(credentialId: Uint8Array | string, sk: Uint8Array): void {\n if (!this.#cacheOptions.enabled) return;\n\n const id = typeof credentialId === 'string' ? credentialId : bytesToHex(credentialId);\n const timeout =\n this.#cacheOptions.timeoutMs !== undefined ? this.#cacheOptions.timeoutMs : 5 * 60 * 1000;\n const expireAt = Date.now() + timeout;\n\n this.#clearCachedEntry();\n\n this.#cachedEntry = {\n id,\n sk: new Uint8Array(sk),\n expireAt,\n };\n\n try {\n this.#scheduleExpiry();\n } catch (error) {\n this.#clearCachedEntry();\n throw error;\n }\n }\n\n /**\n * @param credentialId\n * @returns undefined\n */\n getKey(credentialId: Uint8Array | string): Uint8Array | undefined {\n if (!this.#cacheOptions.enabled) return undefined;\n\n const id = typeof credentialId === 'string' ? credentialId : bytesToHex(credentialId);\n return this.#getCachedKeyIfValid(id);\n }\n\n /**\n * @param credentialId\n */\n clearCachedKey(credentialId: Uint8Array | string): void {\n const id = typeof credentialId === 'string' ? credentialId : bytesToHex(credentialId);\n\n if (this.#cachedEntry && this.#cachedEntry.id === id) {\n this.#clearCachedEntry();\n }\n }\n\n clearAllCachedKeys(): void {\n this.#clearCachedEntry();\n }\n\n /**\n * @param credentialId\n * @returns undefined\n */\n #getCachedKeyIfValid(credentialId: string): Uint8Array | undefined {\n if (!this.#cachedEntry || this.#cachedEntry.id !== credentialId) {\n return undefined;\n }\n\n if (Date.now() < this.#cachedEntry.expireAt) {\n return this.#cachedEntry.sk;\n }\n\n this.#clearCachedEntry();\n return undefined;\n }\n\n #clearCachedEntry(): void {\n if (this.#cachedEntry) {\n this.#clearKey(this.#cachedEntry.sk);\n this.#cachedEntry = null;\n }\n\n if (this.#expiryTimer) {\n clearTimeout(this.#expiryTimer);\n this.#expiryTimer = null;\n }\n }\n\n #scheduleExpiry(): void {\n if (!this.#cachedEntry) return;\n\n const now = Date.now();\n const timeToExpiry = this.#cachedEntry.expireAt - now;\n\n if (timeToExpiry <= 0) {\n this.#clearCachedEntry();\n return;\n }\n\n this.#expiryTimer = setTimeout(() => {\n this.#clearCachedEntry();\n }, timeToExpiry + 1);\n }\n\n /**\n * @param key\n */\n #clearKey(key: Uint8Array): void {\n key?.fill?.(0);\n }\n}\n","/**\n * PRF (Pseudo-Random Function) handler for WebAuthn\n * @packageDocumentation\n */\n\nimport type { GetPrfSecretOptions, PasskeyCreationOptions } from './types.js';\n\nconst PRF_EVAL_INPUT = new TextEncoder().encode('nostr-pwk');\n\n/**\n * @returns PRF\n */\nexport async function isPrfSupported(): Promise<boolean> {\n try {\n const response = await navigator.credentials.get({\n publicKey: {\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n allowCredentials: [],\n userVerification: 'required',\n extensions: { prf: { eval: { first: PRF_EVAL_INPUT } } },\n } as PublicKeyCredentialRequestOptions,\n });\n\n if (!response) return false;\n\n const assertion = response as unknown as {\n getClientExtensionResults: () => {\n prf?: {\n results?: {\n first?: ArrayBuffer;\n };\n };\n };\n };\n\n const res = assertion.getClientExtensionResults()?.prf?.results?.first;\n return !!res;\n } catch {\n return false;\n }\n}\n\n/**\n * @param options\n * @returns Credential\n */\nexport async function createPasskey(options: PasskeyCreationOptions = {}): Promise<Uint8Array> {\n // Node\n const rpName = options.rp?.name || (typeof location !== 'undefined' ? location.host : 'Nosskey');\n const rpId = options.rp?.id;\n const userName = options.user?.name || 'user@example.com';\n const userDisplayName = options.user?.displayName || 'Nosskey user';\n\n const credentialCreationOptions: CredentialCreationOptions = {\n publicKey: {\n rp: {\n name: rpName,\n id: rpId,\n },\n user: {\n id: crypto.getRandomValues(new Uint8Array(16)),\n name: userName,\n displayName: userDisplayName,\n },\n pubKeyCredParams: options.pubKeyCredParams || [{ type: 'public-key', alg: -7 }], // ES256\n authenticatorSelection: options.authenticatorSelection || {\n residentKey: 'required',\n userVerification: 'required',\n },\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n extensions: options.extensions || { prf: {} }, // PRF拡張を要求\n } as PublicKeyCredentialCreationOptions,\n };\n const cred = (await navigator.credentials.create(\n credentialCreationOptions\n )) as PublicKeyCredential;\n\n return new Uint8Array(cred.rawId);\n}\n\n/**\n * ID\n * @param credentialId \n * @param options PRF(rpId、timeout、userVerification)\n * @returns PRF credentialID\n */\nexport async function getPrfSecret(\n credentialId?: Uint8Array,\n options?: GetPrfSecretOptions\n): Promise<{ secret: Uint8Array; id: Uint8Array }> {\n const allowCredentials = credentialId ? [{ type: 'public-key' as const, id: credentialId }] : [];\n\n const requestOptions: PublicKeyCredentialRequestOptions = {\n challenge: crypto.getRandomValues(new Uint8Array(32)),\n allowCredentials,\n userVerification: options?.userVerification || 'required',\n extensions: {\n prf: { eval: { first: PRF_EVAL_INPUT } },\n } as AuthenticationExtensionsClientInputs,\n };\n\n if (options?.rpId) {\n requestOptions.rpId = options.rpId;\n }\n if (options?.timeout) {\n requestOptions.timeout = options.timeout;\n }\n\n const response = await navigator.credentials.get({\n publicKey: requestOptions,\n });\n\n if (!response) {\n throw new Error('Authentication failed');\n }\n\n const assertion = response as unknown as {\n getClientExtensionResults: () => {\n prf?: {\n results?: {\n first?: ArrayBuffer;\n };\n };\n };\n };\n\n const secret = assertion.getClientExtensionResults()?.prf?.results?.first;\n if (!secret) {\n throw new Error('PRF secret not available');\n }\n\n // response credentialId\n const responseId = new Uint8Array((response as PublicKeyCredential).rawId);\n\n return {\n secret: new Uint8Array(secret),\n id: responseId,\n };\n}\n","import { seckeySigner } from 'rx-nostr-crypto';\nimport { KeyCache } from './key-cache.js';\nimport { createPasskey, getPrfSecret, isPrfSupported } from './prf-handler.js';\nimport type {\n GetPrfSecretOptions,\n KeyCacheOptions,\n KeyOptions,\n NosskeyManagerLike,\n NosskeyManagerOptions,\n NostrEvent,\n NostrKeyInfo,\n NostrKeyStorageOptions,\n PasskeyCreationOptions,\n SignOptions,\n} from './types.js';\n/**\n * Nosskey class for Passkey-Derived Nostr Identity\n * @packageDocumentation\n */\nimport { bytesToHex, hexToBytes } from './utils.js';\n\n// salt(\"nostr-key\" UTF-8)\nconst STANDARD_SALT = '6e6f7374722d6b6579';\n\n/**\n * Nosskey - Passkey-Derived Nostr Keys\n */\nexport class NosskeyManager implements NosskeyManagerLike {\n #keyCache: KeyCache;\n\n // NostrKeyInfo\n #currentKeyInfo: NostrKeyInfo | null = null;\n\n // NostrKeyInfo\n #storageOptions: NostrKeyStorageOptions = {\n enabled: true,\n storageKey: 'nosskey_keyinfo',\n };\n\n // PRF\n #prfOptions: GetPrfSecretOptions = {};\n\n /**\n * NosskeyManager\n * @param options\n */\n constructor(options?: NosskeyManagerOptions) {\n // KeyCache\n this.#keyCache = new KeyCache(options?.cacheOptions);\n\n if (options?.storageOptions) {\n this.#storageOptions = { ...this.#storageOptions, ...options.storageOptions };\n }\n\n // option\n const userVerification = options?.prfOptions?.userVerification ?? 'required';\n if (options?.prfOptions) {\n this.#prfOptions = { ...options.prfOptions, userVerification };\n } else {\n this.#prfOptions = { userVerification };\n }\n\n // NostrKeyInfo\n if (this.#storageOptions.enabled) {\n const loadedKeyInfo = this.#loadKeyInfoFromStorage();\n if (loadedKeyInfo) {\n this.#currentKeyInfo = loadedKeyInfo;\n }\n }\n }\n\n /**\n * NostrKeyInfo\n * @param options\n */\n setStorageOptions(options: Partial<NostrKeyStorageOptions>): void {\n this.#storageOptions = { ...this.#storageOptions, ...options };\n\n if (options.enabled === false) {\n this.clearStoredKeyInfo();\n }\n }\n\n /**\n * NostrKeyInfo\n */\n getStorageOptions(): NostrKeyStorageOptions {\n return { ...this.#storageOptions };\n }\n\n /**\n * NostrKeyInfo\n * @param keyInfo NostrKeyInfo\n */\n setCurrentKeyInfo(keyInfo: NostrKeyInfo): void {\n this.#currentKeyInfo = keyInfo;\n\n if (this.#storageOptions.enabled) {\n void this.#saveKeyInfoToStorage(keyInfo);\n }\n }\n\n /**\n * NostrKeyInfo\n */\n getCurrentKeyInfo(): NostrKeyInfo | null {\n // NostrKeyInfo\n if (!this.#currentKeyInfo && this.#storageOptions.enabled) {\n this.#currentKeyInfo = this.#loadKeyInfoFromStorage();\n }\n return this.#currentKeyInfo;\n }\n\n /**\n * NostrKeyInfo\n * @returns NostrKeyInfo\n */\n hasKeyInfo(): boolean {\n if (this.#currentKeyInfo) {\n return true;\n }\n\n if (this.#storageOptions.enabled) {\n const loadedKeyInfo = this.#loadKeyInfoFromStorage();\n if (loadedKeyInfo) {\n this.#currentKeyInfo = loadedKeyInfo;\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * NostrKeyInfo\n * @param keyInfo NostrKeyInfo\n */\n async #saveKeyInfoToStorage(keyInfo: NostrKeyInfo): Promise<void> {\n if (!this.#storageOptions.enabled) return;\n\n const storage =\n this.#storageOptions.storage || (typeof localStorage !== 'undefined' ? localStorage : null);\n\n if (!storage) return;\n\n const key = this.#storageOptions.storageKey || 'nosskey_keyinfo';\n storage.setItem(key, JSON.stringify(keyInfo));\n }\n\n /**\n * NostrKeyInfo\n */\n #loadKeyInfoFromStorage(): NostrKeyInfo | null {\n if (!this.#storageOptions.enabled) return null;\n\n const storage =\n this.#storageOptions.storage || (typeof localStorage !== 'undefined' ? localStorage : null);\n\n if (!storage) return null;\n\n const key = this.#storageOptions.storageKey || 'nosskey_keyinfo';\n const data = storage.getItem(key);\n\n if (!data) return null;\n\n try {\n return JSON.parse(data) as NostrKeyInfo;\n } catch (e) {\n console.error('Failed to parse stored NostrKeyInfo', e);\n return null;\n }\n }\n\n /**\n * NostrKeyInfo\n */\n clearStoredKeyInfo(): void {\n const storage =\n this.#storageOptions.storage || (typeof localStorage !== 'undefined' ? localStorage : null);\n\n if (!storage) return;\n\n const key = this.#storageOptions.storageKey || 'nosskey_keyinfo';\n storage.removeItem(key);\n\n // NostrKeyInfo\n this.#currentKeyInfo = null;\n }\n\n /**\n * NIP-07\n * NostrKeyInfo\n */\n async getPublicKey(): Promise<string> {\n const keyInfo = this.getCurrentKeyInfo();\n if (!keyInfo) {\n throw new Error('No current NostrKeyInfo set');\n }\n return keyInfo.pubkey;\n }\n\n /**\n * NIP-07\n * NostrKeyInfo\n * @param event Nostr\n */\n async signEvent(event: NostrEvent): Promise<NostrEvent> {\n const keyInfo = this.getCurrentKeyInfo();\n if (!keyInfo) {\n throw new Error('No current NostrKeyInfo set');\n }\n return this.signEventWithKeyInfo(event, keyInfo);\n }\n\n /**\n * @param options\n */\n setCacheOptions(options: Partial<KeyCacheOptions>): void {\n this.#keyCache.setCacheOptions(options);\n }\n\n getCacheOptions(): KeyCacheOptions {\n return this.#keyCache.getCacheOptions();\n }\n\n /**\n * @param credentialId\n */\n clearCachedKey(credentialId: Uint8Array | string): void {\n this.#keyCache.clearCachedKey(credentialId);\n }\n\n clearAllCachedKeys(): void {\n this.#keyCache.clearAllCachedKeys();\n }\n\n /**\n * @param options\n * @returns Credential\n */\n async createPasskey(options: PasskeyCreationOptions = {}): Promise<Uint8Array> {\n return createPasskey({\n rp: {\n id: this.#prfOptions.rpId,\n name: this.#prfOptions.rpId,\n },\n authenticatorSelection: {\n userVerification: this.#prfOptions.userVerification,\n },\n ...options,\n });\n }\n\n /**\n * PRF NostrKeyInfo\n * @param credentialId\n * @param options\n */\n async createNostrKey(credentialId?: Uint8Array, options: KeyOptions = {}): Promise<NostrKeyInfo> {\n const { secret: sk, id: responseId } = await getPrfSecret(credentialId, this.#prfOptions);\n\n // secp256k1\n if (sk.every((byte) => byte === 0)) {\n throw new Error('Invalid PRF output: all zeros');\n }\n\n // HEX\n const skHex = bytesToHex(sk);\n\n // rx-nostr-crypto\n const signer = seckeySigner(skHex);\n const publicKey = await signer.getPublicKey();\n\n // NostrKeyInfo\n const keyInfo: NostrKeyInfo = {\n credentialId: bytesToHex(credentialId || responseId),\n pubkey: publicKey,\n salt: STANDARD_SALT, // salt\n ...(options.username && { username: options.username }), // username\n };\n\n return keyInfo;\n }\n\n /**\n * @param event Nostr\n * @param keyInfo NostrKeyInfo\n * @param options\n */\n async signEventWithKeyInfo(\n event: NostrEvent,\n keyInfo: NostrKeyInfo,\n options: SignOptions = {}\n ): Promise<NostrEvent> {\n const { clearMemory = true, tags } = options;\n\n const shouldUseCache = this.#keyCache.isEnabled();\n\n let sk: Uint8Array | undefined;\n\n if (shouldUseCache) {\n sk = this.#keyCache.getKey(keyInfo.credentialId);\n }\n\n if (!sk) {\n const { secret: prfSecret } = await getPrfSecret(\n hexToBytes(keyInfo.credentialId),\n this.#prfOptions\n );\n sk = prfSecret;\n\n if (shouldUseCache) {\n this.#keyCache.setKey(keyInfo.credentialId, sk);\n }\n }\n\n const skHex = bytesToHex(sk);\n\n // rx-nostr-crypto seckeySigner\n const signer = seckeySigner(skHex, { tags });\n const signedEvent = await signer.signEvent(event);\n\n // clearMemory=true\n if (!shouldUseCache && clearMemory) {\n this.#clearKey(sk);\n }\n\n return signedEvent;\n }\n\n /**\n * @param keyInfo NostrKeyInfo\n * @param credentialId NostrKeyInfoのcredentialId\n * @param options\n * @returns \n */\n async exportNostrKey(keyInfo: NostrKeyInfo, credentialId?: Uint8Array): Promise<string> {\n // NostrKeyInfo credentialId\n let usedCredentialId = credentialId;\n\n // credentialId NostrKeyInfo\n if (!usedCredentialId && keyInfo.credentialId) {\n usedCredentialId = hexToBytes(keyInfo.credentialId);\n }\n\n // PRF\n const { secret: sk } = await getPrfSecret(usedCredentialId, this.#prfOptions);\n\n // HEX\n const skHex = bytesToHex(sk);\n\n return skHex;\n }\n\n /**\n * PRF\n */\n async isPrfSupported(): Promise<boolean> {\n return isPrfSupported();\n }\n\n /**\n * @param key\n */\n #clearKey(key: Uint8Array): void {\n key?.fill?.(0);\n }\n}\n","/**\n * Cryptographic utilities for Nosskey\n * @packageDocumentation\n */\n\nconst INFO_BYTES = new TextEncoder().encode('nostr-pwk');\nconst AES_LENGTH = 256; // bits\n\n/**\n * PRF AES-GCM\n * @param secret PRF\n * @param salt\n * @returns AES-GCM\n */\nexport async function deriveAesGcmKey(secret: Uint8Array, salt: Uint8Array): Promise<CryptoKey> {\n const keyMaterial = await crypto.subtle.importKey('raw', secret, 'HKDF', false, ['deriveKey']);\n\n return crypto.subtle.deriveKey(\n { name: 'HKDF', hash: 'SHA-256', salt, info: INFO_BYTES },\n keyMaterial,\n { name: 'AES-GCM', length: AES_LENGTH },\n false,\n ['encrypt', 'decrypt']\n );\n}\n\n/**\n * AES-GCM\n * @param key\n * @param iv\n * @param plaintext\n * @returns\n */\nexport async function aesGcmEncrypt(key: CryptoKey, iv: Uint8Array, plaintext: Uint8Array) {\n const buf = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, plaintext);\n\n const bytes = new Uint8Array(buf);\n return {\n ciphertext: bytes.slice(0, -16),\n tag: bytes.slice(-16),\n };\n}\n\n/**\n * AES-GCM\n * @param key\n * @param iv\n * @param ct\n * @param tag\n * @returns\n */\nexport async function aesGcmDecrypt(\n key: CryptoKey,\n iv: Uint8Array,\n ct: Uint8Array,\n tag: Uint8Array\n): Promise<Uint8Array> {\n const buf = await crypto.subtle.decrypt(\n { name: 'AES-GCM', iv },\n key,\n new Uint8Array([...ct, ...tag])\n );\n return new Uint8Array(buf);\n}\n"],"mappings":"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"}