ns-auth-sdk 1.12.3 → 1.12.4

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/index.cjs CHANGED
@@ -28,6 +28,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  let applesauce_core_helpers = require("applesauce-core/helpers");
29
29
  let _noble_secp256k1 = require("@noble/secp256k1");
30
30
  _noble_secp256k1 = __toESM(_noble_secp256k1);
31
+ let _noble_hashes_sha2 = require("@noble/hashes/sha2");
32
+ let _noble_hashes_pbkdf2 = require("@noble/hashes/pbkdf2");
33
+ let _noble_ciphers_aes = require("@noble/ciphers/aes");
34
+ let _noble_secp256k1_utils = require("@noble/secp256k1/utils");
31
35
 
32
36
  //#region src/utils/utils.ts
33
37
  /**
@@ -197,8 +201,6 @@ const PRF_EVAL_INPUT = new TextEncoder().encode("nostr-pwk");
197
201
  */
198
202
  async function isPrfSupported() {
199
203
  try {
200
- const cryptoObj = typeof window !== "undefined" && window.crypto || globalThis.crypto;
201
- if (!cryptoObj || !cryptoObj.subtle) return false;
202
204
  const response = await navigator.credentials.get({ publicKey: {
203
205
  challenge: crypto.getRandomValues(new Uint8Array(32)),
204
206
  allowCredentials: [],
@@ -282,32 +284,29 @@ async function getPrfSecret(credentialId, options) {
282
284
  * password-protected private key. The private key is wrapped with a password-
283
285
  * derived AES-GCM key and stored alongside the public key (SPKI).
284
286
  *
285
- * This is a minimal, browser-oriented implementation designed to provide a
286
- * practical alternative while keeping crypto surface area focused and safe.
287
+ * This implementation uses @noble libraries for cryptographic operations,
288
+ * ensuring functionality even when Web Crypto API is unavailable.
287
289
  */
288
290
  function toBase64(bytes) {
289
- const arr = new Uint8Array(bytes);
290
291
  let binary = "";
291
- for (let i = 0; i < arr.byteLength; i++) binary += String.fromCharCode(arr[i]);
292
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
292
293
  if (typeof window !== "undefined" && window.btoa) return window.btoa(binary);
293
- return Buffer.from(binary, "binary").toString("base64");
294
+ return btoa(binary);
294
295
  }
295
296
  function fromBase64(b64) {
296
297
  if (typeof window !== "undefined" && window.atob) {
297
- const bin = window.atob(b64);
298
- const bytes = new Uint8Array(bin.length);
299
- for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
300
- return bytes;
301
- }
302
- return new Uint8Array(Buffer.from(b64, "base64"));
298
+ const bin$1 = window.atob(b64);
299
+ const bytes$1 = new Uint8Array(bin$1.length);
300
+ for (let i = 0; i < bin$1.length; i++) bytes$1[i] = bin$1.charCodeAt(i);
301
+ return bytes$1;
302
+ }
303
+ const bin = atob(b64);
304
+ const bytes = new Uint8Array(bin.length);
305
+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
306
+ return bytes;
303
307
  }
304
308
  async function checkPRFSupport() {
305
309
  try {
306
- const cryptoObj = typeof window !== "undefined" && window.crypto || globalThis.crypto;
307
- if (!cryptoObj || !cryptoObj.subtle) {
308
- console.log("Web Crypto API not available, falling back to password-protected key");
309
- return false;
310
- }
311
310
  const supported = (typeof PublicKeyCredential !== "undefined" ? await PublicKeyCredential.getClientCapabilities() : null)?.["extension:prf"] === true;
312
311
  if (supported) console.log("PRF extension is supported.");
313
312
  else console.log("PRF extension is not supported.");
@@ -318,107 +317,43 @@ async function checkPRFSupport() {
318
317
  }
319
318
  }
320
319
  async function generatePasswordProtectedKey(password) {
321
- const cryptoObj = typeof window !== "undefined" && window.crypto || globalThis.crypto;
322
- if (!cryptoObj || !cryptoObj.subtle) throw new Error("Web Crypto API not available");
323
- const kp = await cryptoObj.subtle.generateKey({
324
- name: "ECDSA",
325
- namedCurve: "P-256"
326
- }, true, ["sign"]);
327
- const publicKey = kp.publicKey;
328
- const privateKey = kp.privateKey;
329
- const publicKeySpkiBase64 = toBase64(await cryptoObj.subtle.exportKey("spki", publicKey));
330
- const privateKeyJwk = await cryptoObj.subtle.exportKey("jwk", privateKey);
331
- const jwkStr = JSON.stringify(privateKeyJwk);
332
- const salt = cryptoObj.getRandomValues(new Uint8Array(16));
333
- const pbKey = await cryptoObj.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveKey"]);
334
- const aesKey = await cryptoObj.subtle.deriveKey({
335
- name: "PBKDF2",
336
- salt: salt.slice().buffer,
337
- iterations: 1e5,
338
- hash: "SHA-256"
339
- }, pbKey, {
340
- name: "AES-GCM",
341
- length: 256
342
- }, false, ["encrypt"]);
343
- const iv = cryptoObj.getRandomValues(new Uint8Array(12));
344
- const enc = new TextEncoder().encode(jwkStr);
320
+ const privateKey = _noble_secp256k1.utils.randomPrivateKey();
321
+ const publicKeySpkiBase64 = toBase64(_noble_secp256k1.getPublicKey(privateKey, true));
322
+ const salt = (0, _noble_secp256k1_utils.randomBytes)(16);
323
+ const derivedKey = (0, _noble_hashes_pbkdf2.pbkdf2)(_noble_hashes_sha2.sha256, new TextEncoder().encode(password), salt, {
324
+ c: 1e5,
325
+ dkLen: 32
326
+ });
327
+ const iv = (0, _noble_secp256k1_utils.randomBytes)(12);
345
328
  return {
346
329
  publicKeySpkiBase64,
347
- wrappedPrivateKeyBase64: toBase64(await cryptoObj.subtle.encrypt({
348
- name: "AES-GCM",
349
- iv
350
- }, aesKey, enc)),
351
- saltBase64: toBase64(salt.buffer),
352
- ivBase64: toBase64(iv.buffer)
330
+ wrappedPrivateKeyBase64: toBase64((0, _noble_ciphers_aes.aesGcm)(derivedKey, iv).encrypt(privateKey)),
331
+ saltBase64: toBase64(salt),
332
+ ivBase64: toBase64(iv)
353
333
  };
354
334
  }
355
335
  async function unwrapPasswordProtectedPrivateKey(bundle, password) {
356
- const cryptoObj = typeof window !== "undefined" && window.crypto || globalThis.crypto;
357
- if (!cryptoObj || !cryptoObj.subtle) throw new Error("Web Crypto API not available");
358
336
  const salt = fromBase64(bundle.saltBase64);
359
- const pbKey = await cryptoObj.subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveKey"]);
360
- const aesKey = await cryptoObj.subtle.deriveKey({
361
- name: "PBKDF2",
362
- salt: salt.slice().buffer,
363
- iterations: 1e5,
364
- hash: "SHA-256"
365
- }, pbKey, {
366
- name: "AES-GCM",
367
- length: 256
368
- }, false, ["decrypt"]);
337
+ const derivedKey = (0, _noble_hashes_pbkdf2.pbkdf2)(_noble_hashes_sha2.sha256, new TextEncoder().encode(password), salt, {
338
+ c: 1e5,
339
+ dkLen: 32
340
+ });
369
341
  const iv = fromBase64(bundle.ivBase64);
370
342
  const ct = fromBase64(bundle.wrappedPrivateKeyBase64);
371
- const jwkBytes = await cryptoObj.subtle.decrypt({
372
- name: "AES-GCM",
373
- iv: iv.slice()
374
- }, aesKey, ct.slice());
375
- const jwkStr = new TextDecoder().decode(jwkBytes);
376
- const privateKeyJwk = JSON.parse(jwkStr);
377
- await cryptoObj.subtle.importKey("jwk", privateKeyJwk, {
378
- name: "ECDSA",
379
- namedCurve: "P-256"
380
- }, true, ["sign"]);
381
- const d = privateKeyJwk.d;
382
- if (!d) throw new Error("Missing private scalar in JWK");
383
- return base64UrlToBytes(d);
384
- }
385
- function base64UrlToBytes(base64url) {
386
- let base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
387
- const pad = base64.length % 4;
388
- if (pad) base64 += "=".repeat(4 - pad);
389
- if (typeof window !== "undefined" && window.atob) {
390
- const binary = window.atob(base64);
391
- const bytes = new Uint8Array(binary.length);
392
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
393
- return bytes;
394
- }
395
- return new Uint8Array(Buffer.from(base64, "base64"));
343
+ return (0, _noble_ciphers_aes.aesGcm)(derivedKey, iv).decrypt(ct);
396
344
  }
397
- async function importPublicKeyFromBundle(bundle) {
398
- const cryptoObj = typeof window !== "undefined" && window.crypto || globalThis.crypto;
399
- if (!cryptoObj || !cryptoObj.subtle) throw new Error("Web Crypto API not available");
400
- const spki = fromBase64(bundle.publicKeySpkiBase64);
401
- return await cryptoObj.subtle.importKey("spki", spki, {
402
- name: "ECDSA",
403
- namedCurve: "P-256"
404
- }, true, ["verify"]);
345
+ function importPublicKeyFromBundle(bundle) {
346
+ return fromBase64(bundle.publicKeySpkiBase64);
405
347
  }
406
348
  const DEFAULT_SALT = "nostr-key-derivation";
407
- async function deriveNostrPrivateKey(password, salt = DEFAULT_SALT) {
408
- const cryptoObj = typeof window !== "undefined" && window.crypto || globalThis.crypto;
409
- if (!cryptoObj || !cryptoObj.subtle) throw new Error("Web Crypto API not available");
410
- const encoder = new TextEncoder();
411
- const passwordKey = await cryptoObj.subtle.importKey("raw", encoder.encode(password), "PBKDF2", false, ["deriveBits"]);
412
- const derivedBits = await cryptoObj.subtle.deriveBits({
413
- name: "PBKDF2",
414
- salt: encoder.encode(salt),
415
- iterations: 1e5,
416
- hash: "SHA-256"
417
- }, passwordKey, 256);
418
- return new Uint8Array(derivedBits);
349
+ function deriveNostrPrivateKey(password, salt = DEFAULT_SALT) {
350
+ return (0, _noble_hashes_pbkdf2.pbkdf2)(_noble_hashes_sha2.sha256, new TextEncoder().encode(password), new TextEncoder().encode(salt), {
351
+ c: 1e5,
352
+ dkLen: 32
353
+ });
419
354
  }
420
- async function getPublicKeyFromPassword(password, salt = DEFAULT_SALT) {
421
- return (0, applesauce_core_helpers.getPublicKey)(await deriveNostrPrivateKey(password, salt));
355
+ function getPublicKeyFromPassword(password, salt = DEFAULT_SALT) {
356
+ return (0, applesauce_core_helpers.getPublicKey)(deriveNostrPrivateKey(password, salt));
422
357
  }
423
358
 
424
359
  //#endregion
@@ -701,7 +636,7 @@ var NosskeyManager = class {
701
636
  */
702
637
  async createPasswordProtectedNostrKey(password, options = {}) {
703
638
  const salt = await deriveSaltFromUsername(options.username);
704
- const pubkey = await getPublicKeyFromPassword(password, salt);
639
+ const pubkey = getPublicKeyFromPassword(password, salt);
705
640
  return {
706
641
  credentialId: bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16))),
707
642
  pubkey,
@@ -722,7 +657,7 @@ var NosskeyManager = class {
722
657
  if (isPasswordDerived) {
723
658
  if (shouldUseCache) sk = this.#keyCache.getKey(keyInfo.credentialId);
724
659
  if (!sk && password) {
725
- sk = await deriveNostrPrivateKey(password, keyInfo.salt);
660
+ sk = deriveNostrPrivateKey(password, keyInfo.salt);
726
661
  if (shouldUseCache) this.#keyCache.setKey(keyInfo.credentialId, sk);
727
662
  }
728
663
  if (!sk) throw new Error("Password required - key not in cache. Provide password to sign.");
@@ -763,7 +698,7 @@ var NosskeyManager = class {
763
698
  }
764
699
  if (!keyInfo.passwordProtectedBundle && keyInfo.salt) {
765
700
  if (!options.password) throw new Error("Password is required for password-derived keys");
766
- return bytesToHex(await deriveNostrPrivateKey(options.password, keyInfo.salt));
701
+ return bytesToHex(deriveNostrPrivateKey(options.password, keyInfo.salt));
767
702
  }
768
703
  let usedCredentialId = credentialId;
769
704
  if (!usedCredentialId && keyInfo.credentialId) usedCredentialId = hexToBytes(keyInfo.credentialId);
@@ -788,8 +723,8 @@ var NosskeyManager = class {
788
723
  if (keyInfo.recovery) throw new Error("Recovery already configured");
789
724
  if (!(currentCredentialId || (keyInfo.credentialId ? hexToBytes(keyInfo.credentialId) : void 0))) throw new Error("Credential ID required");
790
725
  const recoverySalt = bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16)));
791
- const recoveryPubkey = await getPublicKeyFromPassword(password, recoverySalt);
792
- const recoverySk = await deriveNostrPrivateKey(password, recoverySalt);
726
+ const recoveryPubkey = getPublicKeyFromPassword(password, recoverySalt);
727
+ const recoverySk = deriveNostrPrivateKey(password, recoverySalt);
793
728
  const signature = this.#signWithKey(recoverySk, keyInfo.pubkey);
794
729
  this.#clearKey(recoverySk);
795
730
  const updatedKeyInfo = {
@@ -824,13 +759,13 @@ var NosskeyManager = class {
824
759
  if (!keyInfo.recovery) throw new Error("No recovery key configured");
825
760
  if (!newCredentialId) throw new Error("New credential ID is required for recovery");
826
761
  const { recoveryPubkey, recoverySalt } = keyInfo.recovery;
827
- if (await getPublicKeyFromPassword(password, recoverySalt) !== recoveryPubkey) throw new Error("Invalid recovery password");
762
+ if (getPublicKeyFromPassword(password, recoverySalt) !== recoveryPubkey) throw new Error("Invalid recovery password");
828
763
  const { secret: newSk } = await getPrfSecret(newCredentialId, this.#prfOptions);
829
764
  const newPubkey = (0, applesauce_core_helpers.getPublicKey)(newSk);
830
765
  this.#clearKey(newSk);
831
766
  const newRecoverySalt = bytesToHex((typeof window !== "undefined" ? window.crypto : globalThis.crypto).getRandomValues(new Uint8Array(16)));
832
- const newRecoveryPubkey = await getPublicKeyFromPassword(password, newRecoverySalt);
833
- const recoverySk = await deriveNostrPrivateKey(password, recoverySalt);
767
+ const newRecoveryPubkey = getPublicKeyFromPassword(password, newRecoverySalt);
768
+ const recoverySk = deriveNostrPrivateKey(password, recoverySalt);
834
769
  const signature = this.#signWithKey(recoverySk, newPubkey);
835
770
  this.#clearKey(recoverySk);
836
771
  const updatedKeyInfo = {