{"version":3,"sources":["../../../src/crypto/ecies/interface.ts"],"sourcesContent":["/**\n * ECIES (Elliptic Curve Integrated Encryption Scheme) Interface\n *\n * @remarks\n * Defines the contract for platform-specific ECIES implementations.\n * All implementations maintain compatibility with the eccrypto format to ensure\n * backward compatibility with existing encrypted data.\n *\n * **Format specification:**\n * `[iv (16 bytes)][ephemPublicKey (65 bytes)][ciphertext (variable)][mac (32 bytes)]`\n *\n * @category Cryptography\n */\n\nimport { CIPHER, CURVE, MAC, FORMAT } from \"./constants\";\nimport { fromHex, toHex } from \"viem\";\n\n/**\n * Represents ECIES encrypted data in eccrypto-compatible format.\n *\n * @remarks\n * This structure maintains backward compatibility with data encrypted using\n * the legacy eccrypto library.\n */\nexport interface ECIESEncrypted {\n  /** Initialization vector (16 bytes) */\n  iv: Uint8Array;\n  /** Ephemeral public key (65 bytes uncompressed) */\n  ephemPublicKey: Uint8Array;\n  /** Encrypted data */\n  ciphertext: Uint8Array;\n  /** Message authentication code (32 bytes) */\n  mac: Uint8Array;\n}\n\n/**\n * Provides ECIES encryption and decryption operations.\n *\n * @remarks\n * Platform-specific implementations handle the underlying cryptographic primitives\n * while maintaining consistent data format across environments.\n *\n * @category Cryptography\n */\nexport interface ECIESProvider {\n  /**\n   * Encrypts data using ECIES with secp256k1.\n   *\n   * @param publicKey - Recipient's public key (65 bytes uncompressed or 33 bytes compressed).\n   *   Obtain via `vana.server.getIdentity(userAddress).public_key`.\n   * @param message - Data to encrypt.\n   * @returns Encrypted data structure compatible with eccrypto format.\n   * @throws {ECIESError} When public key is invalid.\n   *   Verify key format matches secp256k1 requirements.\n   *\n   * @example\n   * ```typescript\n   * const encrypted = await provider.encrypt(\n   *   fromHex(publicKey, 'bytes'),\n   *   new TextEncoder().encode('sensitive data')\n   * );\n   * ```\n   */\n  encrypt(publicKey: Uint8Array, message: Uint8Array): Promise<ECIESEncrypted>;\n\n  /**\n   * Decrypts ECIES encrypted data.\n   *\n   * @param privateKey - Recipient's private key (32 bytes).\n   * @param encrypted - Encrypted data structure from `encrypt()` or legacy eccrypto.\n   * @returns Decrypted message as Uint8Array.\n   * @throws {ECIESError} When MAC verification fails.\n   *   Ensure the private key matches the public key used for encryption.\n   *\n   * @example\n   * ```typescript\n   * const decrypted = await provider.decrypt(\n   *   fromHex(privateKey, 'bytes'),\n   *   encrypted\n   * );\n   * const message = new TextDecoder().decode(decrypted);\n   * ```\n   */\n  decrypt(\n    privateKey: Uint8Array,\n    encrypted: ECIESEncrypted,\n  ): Promise<Uint8Array>;\n\n  /**\n   * Normalizes a public key to uncompressed format (65 bytes with 0x04 prefix).\n   *\n   * @remarks\n   * Strict policy: Only accepts properly formatted compressed (33 bytes) or\n   * uncompressed (65 bytes) public keys. Does not accept 64-byte raw coordinates\n   * to ensure data integrity and prevent masking of malformed inputs.\n   *\n   * @param publicKey - Public key in compressed or uncompressed format\n   * @returns Normalized uncompressed public key (65 bytes with 0x04 prefix)\n   * @throws {Error} When public key format is invalid, including raw coordinates (64 bytes)\n   * @throws {Error} When decompression of compressed key fails\n   *\n   * @example\n   * ```typescript\n   * // Compressed key (33 bytes)\n   * const compressed = new Uint8Array(33);\n   * compressed[0] = 0x02;\n   * const uncompressed = provider.normalizeToUncompressed(compressed);\n   * console.log(uncompressed.length); // 65\n   * console.log(uncompressed[0]); // 0x04\n   *\n   * // Already uncompressed (65 bytes)\n   * const already = provider.normalizeToUncompressed(uncompressedKey);\n   * console.log(already === uncompressedKey); // true (returns same reference)\n   *\n   * // Raw coordinates rejected (64 bytes)\n   * const raw = new Uint8Array(64);\n   * provider.normalizeToUncompressed(raw); // Throws error\n   * ```\n   */\n  normalizeToUncompressed(publicKey: Uint8Array): Uint8Array;\n}\n\n/**\n * Configures ECIES operation behavior.\n */\nexport interface ECIESOptions {\n  /** Use compressed public keys (33 bytes) instead of uncompressed (65 bytes) */\n  useCompressed?: boolean;\n}\n\n/**\n * Represents failures in ECIES cryptographic operations.\n *\n * @remarks\n * Provides specific error codes to help identify and recover from\n * different failure scenarios.\n *\n * @category Errors\n */\nexport class ECIESError extends Error {\n  constructor(\n    message: string,\n    public readonly code:\n      | \"INVALID_KEY\"\n      | \"ENCRYPTION_FAILED\"\n      | \"DECRYPTION_FAILED\"\n      | \"MAC_MISMATCH\"\n      | \"ECDH_FAILED\",\n    public override readonly cause?: Error,\n  ) {\n    super(message);\n    this.name = \"ECIESError\";\n  }\n}\n\n/**\n * Validates if an object conforms to the ECIESEncrypted structure.\n *\n * @param obj - Object to validate.\n * @returns `true` if object is a valid ECIESEncrypted structure.\n *\n * @example\n * ```typescript\n * if (isECIESEncrypted(data)) {\n *   const decrypted = await provider.decrypt(privateKey, data);\n * }\n * ```\n */\nexport function isECIESEncrypted(obj: unknown): obj is ECIESEncrypted {\n  if (!obj || typeof obj !== \"object\") return false;\n  const enc = obj as Record<string, unknown>;\n\n  const isUint8Array = (value: unknown): value is Uint8Array => {\n    return (\n      value instanceof Uint8Array ||\n      (typeof Buffer !== \"undefined\" && Buffer.isBuffer(value))\n    );\n  };\n\n  return (\n    isUint8Array(enc.iv) &&\n    enc.iv.length === CIPHER.IV_LENGTH &&\n    isUint8Array(enc.ephemPublicKey) &&\n    (enc.ephemPublicKey.length === CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH ||\n      enc.ephemPublicKey.length === CURVE.COMPRESSED_PUBLIC_KEY_LENGTH) &&\n    isUint8Array(enc.ciphertext) &&\n    enc.ciphertext.length > 0 &&\n    isUint8Array(enc.mac) &&\n    enc.mac.length === MAC.LENGTH\n  );\n}\n\n/**\n * Serializes ECIESEncrypted to hex string for storage or transmission.\n *\n * @param encrypted - Encrypted data structure from `encrypt()`.\n * @returns Hex string representation.\n *\n * @example\n * ```typescript\n * const hexString = serializeECIES(encrypted);\n * // Store hexString in database or send over network\n * ```\n */\nexport function serializeECIES(encrypted: ECIESEncrypted): string {\n  const combined = new Uint8Array(\n    encrypted.iv.length +\n      encrypted.ephemPublicKey.length +\n      encrypted.ciphertext.length +\n      encrypted.mac.length,\n  );\n\n  let offset = 0;\n  combined.set(encrypted.iv, offset);\n  offset += encrypted.iv.length;\n  combined.set(encrypted.ephemPublicKey, offset);\n  offset += encrypted.ephemPublicKey.length;\n  combined.set(encrypted.ciphertext, offset);\n  offset += encrypted.ciphertext.length;\n  combined.set(encrypted.mac, offset);\n\n  return toHex(combined).slice(2);\n}\n\n/**\n * Deserializes hex string to ECIESEncrypted structure.\n *\n * @param hex - Hex string from `serializeECIES()` or storage.\n * @returns ECIESEncrypted structure ready for decryption.\n * @throws {ECIESError} When hex string format is invalid.\n *   Verify the hex string is complete and uncorrupted.\n *\n * @example\n * ```typescript\n * const encrypted = deserializeECIES(hexString);\n * const decrypted = await provider.decrypt(privateKey, encrypted);\n * ```\n */\nexport function deserializeECIES(hex: string): ECIESEncrypted {\n  const hexWithPrefix = hex.startsWith(\"0x\") ? hex : `0x${hex}`;\n  const bytes = fromHex(hexWithPrefix as `0x${string}`, \"bytes\");\n\n  // Check minimum length before accessing prefix byte\n  // Need at least: IV (16 bytes) + 1 byte for prefix check + MAC (32 bytes) + 1 byte ciphertext\n  const absoluteMinLength = FORMAT.IV_LENGTH + 1 + MAC.LENGTH + 1;\n  if (bytes.length < absoluteMinLength) {\n    throw new ECIESError(\n      `Invalid ECIES data: too short (${bytes.length} bytes, minimum ${absoluteMinLength} bytes required)`,\n      \"DECRYPTION_FAILED\",\n    );\n  }\n\n  // Validate ephemeral public key prefix (must be uncompressed for eccrypto compatibility)\n  const prefix = bytes[FORMAT.EPHEMERAL_KEY_OFFSET];\n\n  if (prefix !== CURVE.PREFIX.UNCOMPRESSED) {\n    throw new ECIESError(\n      `Invalid ephemeral public key: must be uncompressed format (0x04 prefix), got 0x${prefix.toString(16).padStart(2, \"0\")}`,\n      \"DECRYPTION_FAILED\",\n    );\n  }\n\n  const ephemKeySize = CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH;\n\n  const minLength = FORMAT.IV_LENGTH + ephemKeySize + MAC.LENGTH + 1; // +1 for at least 1 byte of ciphertext\n  if (bytes.length < minLength) {\n    throw new ECIESError(\n      `Invalid ECIES data: too short (${bytes.length} bytes, minimum ${minLength} bytes required)`,\n      \"DECRYPTION_FAILED\",\n    );\n  }\n\n  return {\n    iv: bytes.subarray(FORMAT.IV_OFFSET, FORMAT.IV_OFFSET + FORMAT.IV_LENGTH),\n    ephemPublicKey: bytes.subarray(\n      FORMAT.EPHEMERAL_KEY_OFFSET,\n      FORMAT.EPHEMERAL_KEY_OFFSET + ephemKeySize,\n    ),\n    ciphertext: bytes.subarray(\n      FORMAT.EPHEMERAL_KEY_OFFSET + ephemKeySize,\n      bytes.length - MAC.LENGTH,\n    ),\n    mac: bytes.subarray(bytes.length - MAC.LENGTH),\n  };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcA,uBAA2C;AAC3C,kBAA+B;AA4HxB,MAAM,mBAAmB,MAAM;AAAA,EACpC,YACE,SACgB,MAMS,OACzB;AACA,UAAM,OAAO;AARG;AAMS;AAGzB,SAAK,OAAO;AAAA,EACd;AAAA,EAVkB;AAAA,EAMS;AAK7B;AAeO,SAAS,iBAAiB,KAAqC;AACpE,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,QAAM,MAAM;AAEZ,QAAM,eAAe,CAAC,UAAwC;AAC5D,WACE,iBAAiB,cAChB,OAAO,WAAW,eAAe,OAAO,SAAS,KAAK;AAAA,EAE3D;AAEA,SACE,aAAa,IAAI,EAAE,KACnB,IAAI,GAAG,WAAW,wBAAO,aACzB,aAAa,IAAI,cAAc,MAC9B,IAAI,eAAe,WAAW,uBAAM,kCACnC,IAAI,eAAe,WAAW,uBAAM,iCACtC,aAAa,IAAI,UAAU,KAC3B,IAAI,WAAW,SAAS,KACxB,aAAa,IAAI,GAAG,KACpB,IAAI,IAAI,WAAW,qBAAI;AAE3B;AAcO,SAAS,eAAe,WAAmC;AAChE,QAAM,WAAW,IAAI;AAAA,IACnB,UAAU,GAAG,SACX,UAAU,eAAe,SACzB,UAAU,WAAW,SACrB,UAAU,IAAI;AAAA,EAClB;AAEA,MAAI,SAAS;AACb,WAAS,IAAI,UAAU,IAAI,MAAM;AACjC,YAAU,UAAU,GAAG;AACvB,WAAS,IAAI,UAAU,gBAAgB,MAAM;AAC7C,YAAU,UAAU,eAAe;AACnC,WAAS,IAAI,UAAU,YAAY,MAAM;AACzC,YAAU,UAAU,WAAW;AAC/B,WAAS,IAAI,UAAU,KAAK,MAAM;AAElC,aAAO,mBAAM,QAAQ,EAAE,MAAM,CAAC;AAChC;AAgBO,SAAS,iBAAiB,KAA6B;AAC5D,QAAM,gBAAgB,IAAI,WAAW,IAAI,IAAI,MAAM,KAAK,GAAG;AAC3D,QAAM,YAAQ,qBAAQ,eAAgC,OAAO;AAI7D,QAAM,oBAAoB,wBAAO,YAAY,IAAI,qBAAI,SAAS;AAC9D,MAAI,MAAM,SAAS,mBAAmB;AACpC,UAAM,IAAI;AAAA,MACR,kCAAkC,MAAM,MAAM,mBAAmB,iBAAiB;AAAA,MAClF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,SAAS,MAAM,wBAAO,oBAAoB;AAEhD,MAAI,WAAW,uBAAM,OAAO,cAAc;AACxC,UAAM,IAAI;AAAA,MACR,kFAAkF,OAAO,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,MACtH;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,uBAAM;AAE3B,QAAM,YAAY,wBAAO,YAAY,eAAe,qBAAI,SAAS;AACjE,MAAI,MAAM,SAAS,WAAW;AAC5B,UAAM,IAAI;AAAA,MACR,kCAAkC,MAAM,MAAM,mBAAmB,SAAS;AAAA,MAC1E;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,IAAI,MAAM,SAAS,wBAAO,WAAW,wBAAO,YAAY,wBAAO,SAAS;AAAA,IACxE,gBAAgB,MAAM;AAAA,MACpB,wBAAO;AAAA,MACP,wBAAO,uBAAuB;AAAA,IAChC;AAAA,IACA,YAAY,MAAM;AAAA,MAChB,wBAAO,uBAAuB;AAAA,MAC9B,MAAM,SAAS,qBAAI;AAAA,IACrB;AAAA,IACA,KAAK,MAAM,SAAS,MAAM,SAAS,qBAAI,MAAM;AAAA,EAC/C;AACF;","names":[]}