inject.mjs raw

   1  // Test NIP-07 signer — injected into MAIN world.
   2  // Implements window.nostr with hardcoded test keys.
   3  // Uses secp256k1 WASM for BIP-340 Schnorr signing.
   4  
   5  const TEST_SK = '328615b6c92aa6527fc175a67670222daabc69fa2b84c2ded5f6907f78f2b0f8';
   6  const TEST_PK = '5dbeb1d7e84d0a4fb3fac47868438dc9135b35f25e27ae933e306e3584bf69a8';
   7  
   8  const extUrl = new URL('./', import.meta.url).href;
   9  
  10  function hexToBytes(hex) {
  11    const b = new Uint8Array(hex.length / 2);
  12    for (let i = 0; i < b.length; i++) b[i] = parseInt(hex.substr(i * 2, 2), 16);
  13    return b;
  14  }
  15  
  16  function bytesToHex(bytes) {
  17    return Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
  18  }
  19  
  20  const skBytes = hexToBytes(TEST_SK);
  21  
  22  let secp = null;
  23  
  24  async function getSecp() {
  25    if (secp) return secp;
  26    const { initSecp256k1 } = await import(extUrl + 'secp256k1.mjs');
  27    secp = await initSecp256k1(extUrl + 'secp256k1.wasm');
  28    return secp;
  29  }
  30  
  31  // NIP-01 canonical serialization for event ID computation.
  32  function serializeEvent(ev) {
  33    return JSON.stringify([
  34      0,
  35      ev.pubkey,
  36      ev.created_at,
  37      ev.kind,
  38      ev.tags,
  39      ev.content,
  40    ]);
  41  }
  42  
  43  async function sha256hex(str) {
  44    const data = new TextEncoder().encode(str);
  45    const hash = await crypto.subtle.digest('SHA-256', data);
  46    return bytesToHex(new Uint8Array(hash));
  47  }
  48  
  49  window.nostr = {
  50    async getPublicKey() {
  51      return TEST_PK;
  52    },
  53  
  54    async signEvent(event) {
  55      const s = await getSecp();
  56      // Compute event ID per NIP-01.
  57      event.pubkey = TEST_PK;
  58      const id = await sha256hex(serializeEvent(event));
  59      event.id = id;
  60      // BIP-340 Schnorr sign the ID hash.
  61      const sig = s.sign(skBytes, hexToBytes(id));
  62      event.sig = bytesToHex(sig);
  63      return event;
  64    },
  65  
  66    nip04: {
  67      async encrypt(pubkey, plaintext) {
  68        return Promise.reject(new Error('nip04 not implemented in test signer'));
  69      },
  70      async decrypt(pubkey, ciphertext) {
  71        return Promise.reject(new Error('nip04 not implemented in test signer'));
  72      },
  73    },
  74  
  75    nip44: {
  76      async encrypt(pubkey, plaintext) {
  77        // NIP-44 requires ECDH which the secp256k1 WASM doesn't expose.
  78        // For tests that need NIP-44, use nsec auth mode instead.
  79        return Promise.reject(new Error('nip44 not implemented in test signer'));
  80      },
  81      async decrypt(pubkey, ciphertext) {
  82        return Promise.reject(new Error('nip44 not implemented in test signer'));
  83      },
  84    },
  85  };
  86  
  87  console.log('[test-signer] window.nostr installed (pk: ' + TEST_PK.slice(0, 8) + '...)');
  88