{"version":3,"sources":["../../../src/crypto/ecies/node.ts"],"sourcesContent":["/**\n * Node.js implementation of ECIES using native secp256k1 for performance\n *\n * @remarks\n * Uses native secp256k1 bindings for all elliptic curve operations.\n * Uses Node.js crypto module for hashing and AES operations.\n * Provides Uint8Array-only interface with no Buffer exposure.\n */\n\nimport {\n  randomBytes,\n  createHash,\n  createHmac,\n  createCipheriv,\n  createDecipheriv,\n} from \"crypto\";\nimport secp256k1Import from \"secp256k1\";\nimport { BaseECIESUint8 } from \"./base\";\nimport { toHex } from \"viem\";\n\n// Type definition for secp256k1 module\ninterface Secp256k1Module {\n  privateKeyVerify(privateKey: Buffer): boolean;\n  publicKeyCreate(privateKey: Buffer, compressed: boolean): Buffer;\n  publicKeyVerify(publicKey: Buffer): boolean;\n  publicKeyConvert(publicKey: Buffer, compressed: boolean): Buffer;\n  ecdh(\n    publicKey: Buffer,\n    privateKey: Buffer,\n    options: {\n      hashfn: (x: Uint8Array, y: Uint8Array, output?: Uint8Array) => Uint8Array;\n    },\n    output: Buffer,\n  ): Buffer;\n}\n\n// Use the imported secp256k1 module\nconst secp256k1 = secp256k1Import as unknown as Secp256k1Module;\n\n/**\n * Node.js-specific ECIES provider using native secp256k1\n *\n * @remarks\n * This implementation:\n * - Uses native secp256k1 for all EC operations (optimal performance)\n * - Uses Node.js crypto for SHA-512, HMAC, and AES operations\n * - Internally works with Uint8Array\n * - Only uses Buffer at crypto API boundaries\n */\nexport class NodeECIESUint8Provider extends BaseECIESUint8 {\n  // Identity hash function for ECDH - returns raw X coordinate\n  // CRITICAL: Must handle (x, y, output) signature correctly\n  private readonly identityHashFn = (\n    x: Uint8Array,\n    _y: Uint8Array,\n    output?: Uint8Array,\n  ): Uint8Array => {\n    // Copy x into output buffer if provided (prevents allocations)\n    if (output && output.length >= 32) {\n      output.set(x);\n      return output;\n    }\n    return x;\n  };\n  protected generateRandomBytes(length: number): Uint8Array {\n    return new Uint8Array(randomBytes(length));\n  }\n\n  protected verifyPrivateKey(privateKey: Uint8Array): boolean {\n    // Native secp256k1 returns true for valid, false for invalid\n    return secp256k1.privateKeyVerify(Buffer.from(privateKey)) === true;\n  }\n\n  protected createPublicKey(\n    privateKey: Uint8Array,\n    compressed: boolean,\n  ): Uint8Array | null {\n    try {\n      return new Uint8Array(\n        secp256k1.publicKeyCreate(Buffer.from(privateKey), compressed),\n      );\n    } catch {\n      return null;\n    }\n  }\n\n  protected validatePublicKey(publicKey: Uint8Array): boolean {\n    // Native secp256k1 returns true for valid, false for invalid\n    return secp256k1.publicKeyVerify(Buffer.from(publicKey)) === true;\n  }\n\n  protected decompressPublicKey(publicKey: Uint8Array): Uint8Array | null {\n    try {\n      // Convert to uncompressed format (65 bytes)\n      return new Uint8Array(\n        secp256k1.publicKeyConvert(Buffer.from(publicKey), false),\n      );\n    } catch {\n      return null;\n    }\n  }\n\n  protected performECDH(\n    publicKey: Uint8Array,\n    privateKey: Uint8Array,\n  ): Uint8Array {\n    try {\n      // Use pre-allocated buffer for output (32 bytes)\n      const output = Buffer.alloc(32);\n\n      // CRITICAL: Use identity hash to get raw X coordinate\n      // Default would apply SHA256 and break compatibility\n      secp256k1.ecdh(\n        Buffer.from(publicKey),\n        Buffer.from(privateKey),\n        { hashfn: this.identityHashFn },\n        output,\n      );\n\n      return new Uint8Array(output);\n    } catch (error) {\n      throw new Error(\n        `ECDH failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n      );\n    }\n  }\n\n  protected sha512(data: Uint8Array): Uint8Array {\n    // Use Node.js crypto for native performance\n    return new Uint8Array(\n      createHash(\"sha512\").update(Buffer.from(data)).digest(),\n    );\n  }\n\n  protected hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array {\n    // Use Node.js crypto for native performance\n    return new Uint8Array(\n      createHmac(\"sha256\", Buffer.from(key)).update(Buffer.from(data)).digest(),\n    );\n  }\n\n  protected async aesEncrypt(\n    key: Uint8Array,\n    iv: Uint8Array,\n    plaintext: Uint8Array,\n  ): Promise<Uint8Array> {\n    const cipher = createCipheriv(\n      \"aes-256-cbc\",\n      Buffer.from(key),\n      Buffer.from(iv),\n    );\n    const encrypted = Buffer.concat([\n      cipher.update(Buffer.from(plaintext)),\n      cipher.final(),\n    ]);\n    return new Uint8Array(encrypted);\n  }\n\n  protected async aesDecrypt(\n    key: Uint8Array,\n    iv: Uint8Array,\n    ciphertext: Uint8Array,\n  ): Promise<Uint8Array> {\n    const decipher = createDecipheriv(\n      \"aes-256-cbc\",\n      Buffer.from(key),\n      Buffer.from(iv),\n    );\n    const decrypted = Buffer.concat([\n      decipher.update(Buffer.from(ciphertext)),\n      decipher.final(),\n    ]);\n    return new Uint8Array(decrypted);\n  }\n\n  // No Buffer compatibility methods - Uint8Array only public API\n\n  /**\n   * Normalizes a public key to uncompressed format (65 bytes with 0x04 prefix).\n   * Handles compressed (33 bytes) and uncompressed (65 bytes) formats only.\n   *\n   * @remarks\n   * Strict policy: Does not accept 64-byte raw coordinates to avoid masking\n   * malformed data. Callers must provide properly formatted keys.\n   *\n   * @param publicKey - The public key to normalize (33 or 65 bytes)\n   * @returns The normalized uncompressed public key (65 bytes)\n   * @throws {Error} When public key format is invalid or decompression fails\n   */\n  normalizeToUncompressed(publicKey: Uint8Array): Uint8Array {\n    const len = publicKey.length;\n\n    // Already uncompressed\n    if (len === 65 && publicKey[0] === 0x04) {\n      return publicKey;\n    }\n\n    // Compressed - decompress using secp256k1\n    if (len === 33 && (publicKey[0] === 0x02 || publicKey[0] === 0x03)) {\n      const decompressed = this.decompressPublicKey(publicKey);\n      if (!decompressed) {\n        throw new Error(\n          `Failed to decompress public key with prefix ${toHex(publicKey[0])}`,\n        );\n      }\n      return decompressed;\n    }\n\n    // Reject raw coordinates (64 bytes) - require proper formatting\n    if (len === 64) {\n      throw new Error(\n        \"Raw public key coordinates (64 bytes) are not accepted. \" +\n          \"Please provide a properly formatted compressed (33 bytes) or uncompressed (65 bytes) public key.\",\n      );\n    }\n\n    throw new Error(\n      `Invalid public key format: expected compressed (33 bytes) or uncompressed (65 bytes), got ${len} bytes`,\n    );\n  }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASA,oBAMO;AACP,uBAA4B;AAC5B,kBAA+B;AAC/B,kBAAsB;AAmBtB,MAAM,YAAY,iBAAAA;AAYX,MAAM,+BAA+B,2BAAe;AAAA;AAAA;AAAA,EAGxC,iBAAiB,CAChC,GACA,IACA,WACe;AAEf,QAAI,UAAU,OAAO,UAAU,IAAI;AACjC,aAAO,IAAI,CAAC;AACZ,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA,EACU,oBAAoB,QAA4B;AACxD,WAAO,IAAI,eAAW,2BAAY,MAAM,CAAC;AAAA,EAC3C;AAAA,EAEU,iBAAiB,YAAiC;AAE1D,WAAO,UAAU,iBAAiB,OAAO,KAAK,UAAU,CAAC,MAAM;AAAA,EACjE;AAAA,EAEU,gBACR,YACA,YACmB;AACnB,QAAI;AACF,aAAO,IAAI;AAAA,QACT,UAAU,gBAAgB,OAAO,KAAK,UAAU,GAAG,UAAU;AAAA,MAC/D;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEU,kBAAkB,WAAgC;AAE1D,WAAO,UAAU,gBAAgB,OAAO,KAAK,SAAS,CAAC,MAAM;AAAA,EAC/D;AAAA,EAEU,oBAAoB,WAA0C;AACtE,QAAI;AAEF,aAAO,IAAI;AAAA,QACT,UAAU,iBAAiB,OAAO,KAAK,SAAS,GAAG,KAAK;AAAA,MAC1D;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEU,YACR,WACA,YACY;AACZ,QAAI;AAEF,YAAM,SAAS,OAAO,MAAM,EAAE;AAI9B,gBAAU;AAAA,QACR,OAAO,KAAK,SAAS;AAAA,QACrB,OAAO,KAAK,UAAU;AAAA,QACtB,EAAE,QAAQ,KAAK,eAAe;AAAA,QAC9B;AAAA,MACF;AAEA,aAAO,IAAI,WAAW,MAAM;AAAA,IAC9B,SAAS,OAAO;AACd,YAAM,IAAI;AAAA,QACR,gBAAgB,iBAAiB,QAAQ,MAAM,UAAU,eAAe;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AAAA,EAEU,OAAO,MAA8B;AAE7C,WAAO,IAAI;AAAA,UACT,0BAAW,QAAQ,EAAE,OAAO,OAAO,KAAK,IAAI,CAAC,EAAE,OAAO;AAAA,IACxD;AAAA,EACF;AAAA,EAEU,WAAW,KAAiB,MAA8B;AAElE,WAAO,IAAI;AAAA,UACT,0BAAW,UAAU,OAAO,KAAK,GAAG,CAAC,EAAE,OAAO,OAAO,KAAK,IAAI,CAAC,EAAE,OAAO;AAAA,IAC1E;AAAA,EACF;AAAA,EAEA,MAAgB,WACd,KACA,IACA,WACqB;AACrB,UAAM,aAAS;AAAA,MACb;AAAA,MACA,OAAO,KAAK,GAAG;AAAA,MACf,OAAO,KAAK,EAAE;AAAA,IAChB;AACA,UAAM,YAAY,OAAO,OAAO;AAAA,MAC9B,OAAO,OAAO,OAAO,KAAK,SAAS,CAAC;AAAA,MACpC,OAAO,MAAM;AAAA,IACf,CAAC;AACD,WAAO,IAAI,WAAW,SAAS;AAAA,EACjC;AAAA,EAEA,MAAgB,WACd,KACA,IACA,YACqB;AACrB,UAAM,eAAW;AAAA,MACf;AAAA,MACA,OAAO,KAAK,GAAG;AAAA,MACf,OAAO,KAAK,EAAE;AAAA,IAChB;AACA,UAAM,YAAY,OAAO,OAAO;AAAA,MAC9B,SAAS,OAAO,OAAO,KAAK,UAAU,CAAC;AAAA,MACvC,SAAS,MAAM;AAAA,IACjB,CAAC;AACD,WAAO,IAAI,WAAW,SAAS;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,wBAAwB,WAAmC;AACzD,UAAM,MAAM,UAAU;AAGtB,QAAI,QAAQ,MAAM,UAAU,CAAC,MAAM,GAAM;AACvC,aAAO;AAAA,IACT;AAGA,QAAI,QAAQ,OAAO,UAAU,CAAC,MAAM,KAAQ,UAAU,CAAC,MAAM,IAAO;AAClE,YAAM,eAAe,KAAK,oBAAoB,SAAS;AACvD,UAAI,CAAC,cAAc;AACjB,cAAM,IAAI;AAAA,UACR,mDAA+C,mBAAM,UAAU,CAAC,CAAC,CAAC;AAAA,QACpE;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAGA,QAAI,QAAQ,IAAI;AACd,YAAM,IAAI;AAAA,QACR;AAAA,MAEF;AAAA,IACF;AAEA,UAAM,IAAI;AAAA,MACR,6FAA6F,GAAG;AAAA,IAClG;AAAA,EACF;AACF;","names":["secp256k1Import"]}