UNPKG

@opendatalabs/vana-sdk

Version:

A TypeScript library for interacting with Vana Network smart contracts.

4 lines 1.39 MB
{ "version": 3, "sources": ["../src/platform/shared/pgp-utils.ts", "../src/platform/shared/error-utils.ts", "../src/utils/lazy-import.ts", "../src/utils/crypto-utils.ts", "../src/crypto/services/WalletKeyEncryptionService.ts", "../src/crypto/ecies/constants.ts", "../src/crypto/ecies/interface.ts", "../src/crypto/ecies/utils.ts", "../src/crypto/ecies/base.ts", "../src/crypto/ecies/browser.ts", "../src/platform/browser.ts", "../src/index.node.ts", "../src/errors.ts", "../src/contracts/contractController.ts", "../src/generated/abi/ComputeEngineImplementation.ts", "../src/generated/abi/DataRegistryImplementation.ts", "../src/generated/abi/TeePoolPhalaImplementation.ts", "../src/generated/abi/DataPortabilityPermissionsImplementation.ts", "../src/generated/abi/DataPortabilityServersImplementation.ts", "../src/generated/abi/DataPortabilityGranteesImplementation.ts", "../src/generated/abi/DataRefinerRegistryImplementation.ts", "../src/generated/abi/QueryEngineImplementation.ts", "../src/generated/abi/ComputeInstructionRegistryImplementation.ts", "../src/generated/abi/TeePoolEphemeralStandardImplementation.ts", "../src/generated/abi/TeePoolPersistentStandardImplementation.ts", "../src/generated/abi/TeePoolPersistentGpuImplementation.ts", "../src/generated/abi/TeePoolDedicatedStandardImplementation.ts", "../src/generated/abi/TeePoolDedicatedGpuImplementation.ts", "../src/generated/abi/VanaEpochImplementation.ts", "../src/generated/abi/DLPRegistryImplementation.ts", "../src/generated/abi/DLPTreasuryImplementation.ts", "../src/generated/abi/VanaTreasuryImplementation.ts", "../src/generated/abi/DLPRegistryTreasuryImplementation.ts", "../src/generated/abi/VanaPoolStakingImplementation.ts", "../src/generated/abi/VanaPoolEntityImplementation.ts", "../src/generated/abi/VanaPoolTreasuryImplementation.ts", "../src/generated/abi/DATImplementation.ts", "../src/generated/abi/DATFactoryImplementation.ts", "../src/generated/abi/DATPausableImplementation.ts", "../src/generated/abi/DATVotesImplementation.ts", "../src/generated/abi/index.ts", "../src/generated/addresses.ts", "../src/core/client.ts", "../src/config/chains.ts", "../src/config/default-services.ts", "../src/chains/definitions.ts", "../src/types/storage.ts", "../src/auth/web3-signed-builder.ts", "../src/utils/encoding.ts", "../src/storage/providers/vana-storage.ts", "../src/storage/default.ts", "../src/storage/providers/r2.ts", "../src/storage/providers/google-drive.ts", "../src/storage/providers/dropbox.ts", "../src/storage/providers/ipfs.ts", "../src/storage/providers/pinata.ts", "../src/storage/providers/callback-storage.ts", "../src/storage/manager.ts", "../src/platform/node.ts", "../src/platform/shared/stream-utils.ts", "../src/crypto/ecies/node.ts", "../src/crypto/ecies/index.ts", "../src/platform/utils.ts", "../src/platform/browser-safe.ts", "../src/crypto/keys/derive.ts", "../src/crypto/envelope/openpgp.ts", "../src/auth/web3-signed.ts", "../src/auth/errors.ts", "../src/auth/pkce.ts", "../src/auth/token-store.ts", "../src/auth/oauth-client.ts", "../src/protocol/eip712.ts", "../src/protocol/personal-server-registration.ts", "../src/protocol/personal-server-lite-owner-binding.ts", "../src/account/personal-server-registration.ts", "../src/account/personal-server-lite-owner-binding.ts", "../src/protocol/grants.ts", "../src/protocol/scopes.ts", "../src/protocol/data-file.ts", "../src/protocol/gateway.ts", "../src/types/ps-errors.ts"], "sourcesContent": ["/**\n * Shared PGP utilities for platform adapters\n *\n * IMPORTANT: This module contains NO IMPORTS to avoid affecting bundle loading.\n * All functions are pure utilities that can be safely shared across platforms.\n */\n\n/**\n * Standard OpenPGP configuration for consistent behavior across platforms\n * Uses enum values instead of importing openpgp to avoid loading issues\n */\nexport const STANDARD_PGP_CONFIG = {\n preferredCompressionAlgorithm: 2, // zlib (openpgp.enums.compression.zlib)\n preferredSymmetricAlgorithm: 7, // aes256 (openpgp.enums.symmetric.aes256)\n} as const;\n\n/**\n * Process PGP key generation options with sensible defaults\n *\n * @param options - Optional key generation parameters\n * @param options.name - The name for the PGP key (defaults to \"Vana User\")\n * @param options.email - The email for the PGP key (defaults to \"user@vana.org\")\n * @param options.passphrase - Optional passphrase to protect the private key\n * @returns Processed options with defaults applied\n */\nexport function processPGPKeyOptions(options?: {\n name?: string;\n email?: string;\n passphrase?: string;\n}) {\n return {\n name: options?.name ?? \"Vana User\",\n email: options?.email ?? \"user@vana.org\",\n passphrase: options?.passphrase,\n };\n}\n\n/**\n * Get standard PGP key generation parameters\n * Combines default values with standard configuration\n *\n * @param options - Optional key generation parameters\n * @param options.name - The name for the PGP key (defaults to \"Vana User\")\n * @param options.email - The email for the PGP key (defaults to \"user@vana.org\")\n * @param options.passphrase - Optional passphrase to protect the private key\n * @returns Complete key generation parameters object\n */\nexport function getPGPKeyGenParams(options?: {\n name?: string;\n email?: string;\n passphrase?: string;\n}) {\n const { name, email, passphrase } = processPGPKeyOptions(options);\n\n return {\n type: \"rsa\" as const,\n rsaBits: 2048,\n userIDs: [{ name, email }],\n passphrase,\n config: STANDARD_PGP_CONFIG,\n };\n}\n", "/**\n * Shared error utilities for platform adapters\n *\n * IMPORTANT: This module contains NO IMPORTS to avoid affecting bundle loading.\n * All functions are pure utilities that can be safely shared across platforms.\n */\n\n/**\n * Wrap platform-specific errors with consistent messaging\n * Provides consistent error formatting across all crypto operations\n *\n * @param operation The operation that failed (e.g., \"encryption\", \"decryption\")\n * @param error The original error that occurred\n * @returns Wrapped error with consistent format\n */\nexport function wrapCryptoError(operation: string, error: unknown): Error {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n return new Error(`${operation} failed: ${message}`);\n}\n\n/**\n * Validate encrypted data structure has required fields\n * Ensures encrypted data objects contain the expected properties\n *\n * @param data The data structure to validate\n * @throws Error if data structure is invalid\n */\nexport function validateEncryptedDataStructure(data: unknown): void {\n if (!data || typeof data !== \"object\") {\n throw new Error(\"Invalid encrypted data format\");\n }\n\n const obj = data as Record<string, unknown>;\n if (!obj.encrypted || !obj.iv || !obj.ephemeralPublicKey) {\n throw new Error(\"Invalid encrypted data format\");\n }\n}\n", "/**\n * Provides lazy module loading to avoid Temporal Dead Zone issues.\n *\n * @remarks\n * This module implements a caching lazy import pattern to work around Turbopack's\n * strict module initialization. Dependencies that access globals during initialization\n * must be dynamically imported to prevent TDZ errors in Next.js environments.\n *\n * @see {@link https://github.com/vercel/next.js/issues/82632 | Turbopack TDZ Issue}\n *\n * @category Utilities\n * @module utils/lazy-import\n */\n\n/**\n * Creates a cached lazy import function for deferred module loading.\n *\n * @remarks\n * Caches the import promise (not the module) to prevent race conditions\n * during concurrent first calls. On import failure, clears the cache to\n * allow retry on next attempt.\n *\n * @typeParam T - The type of the module being imported\n *\n * @param importFn - Function that returns a dynamic import promise.\n * Should be a dynamic import expression like `() => import('module')`.\n * @returns A function that returns the cached import promise\n *\n * @example\n * ```typescript\n * // Create lazy loader for heavy crypto library\n * const getOpenPGP = lazyImport(() => import('openpgp'));\n *\n * // Use when needed (first call triggers import)\n * const openpgp = await getOpenPGP();\n * const encrypted = await openpgp.encrypt(data);\n *\n * // Subsequent calls return cached promise\n * const openpgp2 = await getOpenPGP(); // Same instance\n * ```\n *\n * @category Utilities\n */\nexport function lazyImport<T>(importFn: () => Promise<T>): () => Promise<T> {\n let cached: Promise<T> | null = null;\n\n return () => {\n cached ??= importFn().catch((err) => {\n // Clear cache on error so next attempt can retry\n cached = null;\n throw new Error(\"Failed to load module\", { cause: err });\n });\n return cached;\n };\n}\n", "/**\n * Provides platform-agnostic cryptographic utility functions.\n *\n * @remarks\n * This module contains utility functions for cryptographic operations that work\n * consistently across Node.js and browser environments. All functions use `Uint8Array`\n * exclusively for binary data to ensure cross-platform compatibility.\n *\n * @category Cryptography\n */\n\nimport { fromHex } from \"viem\";\n\n/**\n * Processes a wallet public key for cryptographic operations.\n *\n * @remarks\n * Converts hex string public keys to Uint8Array format.\n * For normalization to uncompressed format, use the crypto provider's\n * normalizeToUncompressed method.\n *\n * @param publicKey - The wallet public key as hex string or byte array.\n * @returns The public key as a Uint8Array.\n *\n * @example\n * ```typescript\n * const keyBytes = processWalletPublicKey(\"0x04...\");\n * const normalized = provider.normalizeToUncompressed(keyBytes);\n * ```\n */\nexport function processWalletPublicKey(\n publicKey: string | Uint8Array,\n): Uint8Array {\n // Convert to bytes if hex string\n return typeof publicKey === \"string\"\n ? fromHex(\n (publicKey.startsWith(\"0x\")\n ? publicKey\n : `0x${publicKey}`) as `0x${string}`,\n \"bytes\",\n )\n : publicKey;\n}\n\n/**\n * Processes a wallet private key for cryptographic operations.\n *\n * @param privateKey - The wallet private key as hex string or byte array.\n * @returns The private key as a Uint8Array.\n *\n * @example\n * ```typescript\n * const key = processWalletPrivateKey(\"0x...\");\n * console.log(key.length); // 32 (secp256k1 private key)\n * ```\n */\nexport function processWalletPrivateKey(\n privateKey: string | Uint8Array,\n): Uint8Array {\n // Convert to bytes\n return typeof privateKey === \"string\"\n ? fromHex(\n (privateKey.startsWith(\"0x\")\n ? privateKey\n : `0x${privateKey}`) as `0x${string}`,\n \"bytes\",\n )\n : privateKey;\n}\n\n/**\n * Parses legacy eccrypto-format encrypted data buffer.\n * Format: [iv(16)][ephemPublicKey(65)][ciphertext(variable)][mac(32)]\n *\n * @param encryptedBuffer - Buffer containing encrypted data in eccrypto format\n * @returns Parsed encrypted data components\n */\nexport function parseEncryptedDataBuffer(encryptedBuffer: Uint8Array): {\n iv: Uint8Array;\n ephemPublicKey: Uint8Array;\n ciphertext: Uint8Array;\n mac: Uint8Array;\n} {\n return {\n iv: encryptedBuffer.slice(0, 16),\n ephemPublicKey: encryptedBuffer.slice(16, 81), // 65 bytes for uncompressed public key\n ciphertext: encryptedBuffer.slice(81, -32),\n mac: encryptedBuffer.slice(-32),\n };\n}\n\n/**\n * Generates a deterministic seed from a message for key derivation\n *\n * @param message - Message to derive seed from\n * @returns Seed as Uint8Array\n */\nexport function generateSeed(message: string): Uint8Array {\n // Use encoding utils for consistent string-to-bytes conversion\n const encoder = new TextEncoder();\n return encoder.encode(message);\n}\n\n/**\n * Compares two Uint8Arrays for equality\n *\n * @param a - First array\n * @param b - Second array\n * @returns True if arrays are equal\n */\nexport function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Creates a copy of a Uint8Array\n *\n * @param bytes - Array to copy\n * @returns New array with same contents\n */\nexport function copyBytes(bytes: Uint8Array): Uint8Array {\n return new Uint8Array(bytes);\n}\n\n/**\n * Validates a secp256k1 public key format\n *\n * @param publicKey - Public key to validate\n * @returns True if valid format (33, 65, or 64 bytes)\n */\nexport function isValidPublicKeyFormat(publicKey: Uint8Array): boolean {\n const len = publicKey.length;\n\n // Compressed (33 bytes)\n if (len === 33) {\n return publicKey[0] === 0x02 || publicKey[0] === 0x03;\n }\n\n // Uncompressed (65 bytes)\n if (len === 65) {\n return publicKey[0] === 0x04;\n }\n\n // Raw coordinates (64 bytes - no prefix)\n if (len === 64) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Validates a secp256k1 private key format\n *\n * @param privateKey - Private key to validate\n * @returns True if valid format (32 bytes)\n */\nexport function isValidPrivateKeyFormat(privateKey: Uint8Array): boolean {\n return privateKey.length === 32;\n}\n\n/**\n * Asserts that a public key is in uncompressed format (65 bytes with 0x04 prefix).\n * This validation function only checks format, it does not transform keys.\n * For key normalization (including decompression), use the crypto provider's\n * normalizeToUncompressed method.\n *\n * @param publicKey - Public key to validate\n * @throws {Error} When public key is not in uncompressed format\n */\nexport function assertUncompressedPublicKey(publicKey: Uint8Array): void {\n if (publicKey.length !== 65) {\n throw new Error(\n `Public key must be uncompressed (65 bytes), got ${publicKey.length} bytes. ` +\n `Use provider.normalizeToUncompressed() to convert compressed keys.`,\n );\n }\n\n if (publicKey[0] !== 0x04) {\n throw new Error(\n `Uncompressed public key must start with 0x04 prefix, got 0x${publicKey[0].toString(16).padStart(2, \"0\")}`,\n );\n }\n}\n", "/**\n * Service for wallet key encryption and decryption operations.\n *\n * @remarks\n * This service separates business logic (wallet key processing) from crypto primitives\n * (ECIES operations). It handles key normalization, data conversion, and format transformation\n * while delegating actual cryptographic operations to the provided ECIES provider.\n *\n * @category Cryptography\n * @internal\n */\n\nimport type { ECIESProvider, ECIESEncrypted } from \"../ecies/interface\";\nimport {\n processWalletPublicKey,\n processWalletPrivateKey,\n parseEncryptedDataBuffer,\n} from \"../../utils/crypto-utils\";\nimport { stringToBytes, bytesToString, toHex, fromHex, concat } from \"viem\";\n\nexport interface WalletKeyEncryptionServiceConfig {\n /** ECIES provider for encryption/decryption */\n eciesProvider: ECIESProvider;\n}\n\n/**\n * Service for wallet key encryption and decryption operations\n *\n * @remarks\n * This service encapsulates the business logic for wallet key operations,\n * delegating actual cryptographic operations to the provided ECIES provider.\n * It handles key normalization, data conversion, and format transformation.\n *\n * @internal\n */\nexport class WalletKeyEncryptionService {\n private readonly eciesProvider: ECIESProvider;\n\n constructor(config: WalletKeyEncryptionServiceConfig) {\n this.eciesProvider = config.eciesProvider;\n }\n\n /**\n * Encrypts data using a wallet's public key.\n *\n * @param data - The plaintext message to encrypt for the wallet owner.\n * @param publicKey - The recipient wallet's public key for encryption.\n * @returns A promise that resolves to the encrypted data as a hex string.\n * @throws {Error} When encryption fails due to invalid key format.\n *\n * @example\n * ```typescript\n * const encrypted = await processor.encryptWithWalletPublicKey(\n * \"Secret message\",\n * \"0x04...\" // 65-byte uncompressed public key\n * );\n * console.log(`Encrypted: ${encrypted}`);\n * ```\n */\n async encryptWithWalletPublicKey(\n data: string,\n publicKey: string | Uint8Array,\n ): Promise<string> {\n // Process the public key to ensure correct format\n const publicKeyBytes = processWalletPublicKey(publicKey);\n\n // Normalize to uncompressed format using the provider\n // This handles compressed keys, raw coordinates, and validates the format\n const normalizedKey =\n this.eciesProvider.normalizeToUncompressed(publicKeyBytes);\n\n // Convert string data to bytes\n const dataBytes = stringToBytes(data);\n\n // Perform ECIES encryption\n const encrypted = await this.eciesProvider.encrypt(\n normalizedKey,\n dataBytes,\n );\n\n // Concatenate all components for legacy format compatibility\n const result = concat([\n encrypted.iv,\n encrypted.ephemPublicKey,\n encrypted.ciphertext,\n encrypted.mac,\n ]);\n\n // Return as hex string without 0x prefix for API compatibility\n return toHex(result).slice(2);\n }\n\n /**\n * Decrypts data using a wallet's private key.\n *\n * @param encryptedData - The hex-encoded encrypted data to decrypt.\n * @param privateKey - The wallet's private key for decryption.\n * @returns A promise that resolves to the decrypted plaintext message.\n * @throws {Error} When decryption fails due to invalid data or key format.\n *\n * @example\n * ```typescript\n * const decrypted = await processor.decryptWithWalletPrivateKey(\n * encryptedHexString,\n * \"0x...\" // 32-byte private key\n * );\n * console.log(`Decrypted: ${decrypted}`);\n * ```\n */\n async decryptWithWalletPrivateKey(\n encryptedData: string,\n privateKey: string | Uint8Array,\n ): Promise<string> {\n // Process the private key to ensure correct format\n const privateKeyBytes = processWalletPrivateKey(privateKey);\n\n // Convert hex string to bytes and parse encrypted components\n const prefixedHex = encryptedData.startsWith(\"0x\")\n ? encryptedData\n : `0x${encryptedData}`;\n const encryptedBytes = fromHex(prefixedHex as `0x${string}`, \"bytes\");\n const encrypted = parseEncryptedDataBuffer(encryptedBytes);\n\n // Perform ECIES decryption\n const decrypted = await this.eciesProvider.decrypt(\n privateKeyBytes,\n encrypted,\n );\n\n // Convert bytes back to string\n return bytesToString(decrypted);\n }\n\n /**\n * Encrypts a Uint8Array with a wallet public key\n *\n * @param data - Binary data to encrypt\n * @param publicKey - Public key as hex string or Uint8Array\n * @returns Encrypted data structure\n */\n async encryptBinary(\n data: Uint8Array,\n publicKey: string | Uint8Array,\n ): Promise<ECIESEncrypted> {\n const publicKeyBytes = processWalletPublicKey(publicKey);\n const normalizedKey =\n this.eciesProvider.normalizeToUncompressed(publicKeyBytes);\n return this.eciesProvider.encrypt(normalizedKey, data);\n }\n\n /**\n * Decrypts to a Uint8Array with a wallet private key\n *\n * @param encrypted - Encrypted data structure\n * @param privateKey - Private key as hex string or Uint8Array\n * @returns Decrypted binary data\n */\n async decryptBinary(\n encrypted: ECIESEncrypted,\n privateKey: string | Uint8Array,\n ): Promise<Uint8Array> {\n const privateKeyBytes = processWalletPrivateKey(privateKey);\n return this.eciesProvider.decrypt(privateKeyBytes, encrypted);\n }\n\n /**\n * Gets the underlying ECIES provider\n *\n * @returns The ECIES provider instance\n */\n getECIESProvider(): ECIESProvider {\n return this.eciesProvider;\n }\n}\n", "/**\n * ECIES Constants and Format Specification\n *\n * These constants define the eccrypto-compatible ECIES format used throughout the SDK.\n * Maintaining these exact values ensures backward compatibility with data encrypted\n * using the original eccrypto library.\n */\n\n/**\n * Elliptic curve parameters\n */\nexport const CURVE = {\n /** The elliptic curve used (secp256k1 - same as Bitcoin/Ethereum) */\n name: \"secp256k1\",\n /** Private key length in bytes */\n PRIVATE_KEY_LENGTH: 32,\n /** Compressed public key length in bytes (0x02 or 0x03 prefix + 32 bytes) */\n COMPRESSED_PUBLIC_KEY_LENGTH: 33,\n /** Uncompressed public key length in bytes (0x04 prefix + 64 bytes) */\n UNCOMPRESSED_PUBLIC_KEY_LENGTH: 65,\n /** ECDH shared secret X coordinate length */\n SHARED_SECRET_LENGTH: 32,\n /** Public key prefixes */\n PREFIX: {\n /** Uncompressed public key prefix */\n UNCOMPRESSED: 0x04,\n /** Compressed public key prefix for even Y */\n COMPRESSED_EVEN: 0x02,\n /** Compressed public key prefix for odd Y */\n COMPRESSED_ODD: 0x03,\n },\n /** X coordinate starts at byte 1 (after prefix) */\n X_COORDINATE_OFFSET: 1,\n /** X coordinate ends at byte 33 (1 + 32) */\n X_COORDINATE_END: 33,\n} as const;\n\n/**\n * Symmetric encryption parameters (AES-256-CBC)\n */\nexport const CIPHER = {\n /** Cipher algorithm - must match eccrypto */\n algorithm: \"aes-256-cbc\",\n /** AES key length in bytes */\n KEY_LENGTH: 32,\n /** Initialization vector length in bytes */\n IV_LENGTH: 16,\n /** Block size for AES */\n BLOCK_SIZE: 16,\n} as const;\n\n/**\n * Key derivation function parameters\n */\nexport const KDF = {\n /** Hash algorithm for key derivation - must match eccrypto */\n algorithm: \"sha512\",\n /** Output length of SHA-512 in bytes */\n OUTPUT_LENGTH: 64,\n /** Encryption key slice (first 32 bytes of KDF output) */\n ENCRYPTION_KEY_OFFSET: 0,\n ENCRYPTION_KEY_LENGTH: 32,\n /** MAC key slice (last 32 bytes of KDF output) */\n MAC_KEY_OFFSET: 32,\n MAC_KEY_LENGTH: 32,\n} as const;\n\n/**\n * Message authentication code parameters\n */\nexport const MAC = {\n /** MAC algorithm - must match eccrypto */\n algorithm: \"sha256\",\n /** HMAC-SHA256 output length in bytes */\n LENGTH: 32,\n} as const;\n\n/**\n * ECIES encrypted data format offsets and lengths\n * Format: [iv(16)][ephemPublicKey(65)][ciphertext(variable)][mac(32)]\n */\nexport const FORMAT = {\n /** Offsets for each component in serialized format */\n IV_OFFSET: 0,\n IV_LENGTH: CIPHER.IV_LENGTH,\n\n /** Ephemeral public key (always uncompressed in eccrypto format) */\n EPHEMERAL_KEY_OFFSET: CIPHER.IV_LENGTH,\n EPHEMERAL_KEY_LENGTH: CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH,\n\n /** Ciphertext starts after IV and ephemeral key */\n CIPHERTEXT_OFFSET: CIPHER.IV_LENGTH + CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH,\n\n /** MAC is always the last 32 bytes */\n MAC_LENGTH: MAC.LENGTH,\n\n /** Minimum size of encrypted data (IV + ephemKey + MAC, no ciphertext) */\n MIN_ENCRYPTED_LENGTH:\n CIPHER.IV_LENGTH + CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH + MAC.LENGTH,\n\n /**\n * Helper to calculate total length of encrypted data\n *\n * @param ciphertextLength - Length of the ciphertext portion\n * @returns Total length including all components\n */\n getTotalLength: (ciphertextLength: number) =>\n CIPHER.IV_LENGTH +\n CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH +\n ciphertextLength +\n MAC.LENGTH,\n} as const;\n\n/**\n * Security constants for data clearing\n */\nexport const SECURITY = {\n /** Overwrite patterns for secure data clearing */\n CLEAR_PATTERNS: {\n ZEROS: 0x00,\n ONES: 0xff,\n /** Pattern multiplier for third pass */\n PATTERN_MULTIPLIER: 7,\n /** Pattern offset for third pass */\n PATTERN_OFFSET: 13,\n },\n} as const;\n", "/**\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", "/**\n * Utility functions for ECIES operations\n *\n * Provides conversion utilities between different data formats\n * to bridge platform-specific implementations.\n */\n\n/**\n * Checks if two Uint8Arrays are equal in constant time\n *\n * @param a - First array to compare\n * @param b - Second array to compare\n * @returns `true` if arrays are equal\n */\nexport function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.length !== b.length) return false;\n let result = 0;\n for (let i = 0; i < a.length; i++) {\n result |= a[i] ^ b[i];\n }\n return result === 0;\n}\n\n/**\n * Converts Buffer to Uint8Array (for Node.js compatibility layer)\n * In browser, this is a no-op if already Uint8Array\n *\n * @param buffer - Buffer or Uint8Array to convert\n * @returns Uint8Array representation\n */\nexport function bufferToBytes(buffer: Buffer | Uint8Array): Uint8Array {\n if (buffer instanceof Uint8Array) {\n return buffer;\n }\n // Node.js Buffer is a subclass of Uint8Array\n return new Uint8Array(buffer);\n}\n\n/**\n * Converts Uint8Array to Buffer (for Node.js compatibility layer)\n * Only available in Node.js environment\n *\n * @param bytes - Uint8Array to convert to Buffer\n * @returns Buffer representation\n */\nexport function bytesToBuffer(bytes: Uint8Array): Buffer {\n if (typeof Buffer === \"undefined\") {\n throw new Error(\"Buffer is not available in browser environment\");\n }\n return Buffer.from(bytes);\n}\n", "import type { ECIESProvider, ECIESEncrypted } from \"./interface\";\nimport { ECIESError, isECIESEncrypted } from \"./interface\";\nimport { CURVE, CIPHER, KDF } from \"./constants\";\nimport { constantTimeEqual } from \"./utils\";\nimport { concat } from \"viem\";\n\n/**\n * Provides shared ECIES encryption logic across platforms using Uint8Array.\n *\n * @remarks\n * Platform implementations extend this class and provide crypto primitives.\n * The base class handles the ECIES protocol flow while maintaining\n * compatibility with the eccrypto data format.\n *\n * **Implementation details:**\n * - KDF: SHA-512(shared_secret) \u2192 encKey (32B) || macKey (32B)\n * - Cipher: AES-256-CBC with random 16-byte IV\n * - MAC: HMAC-SHA256(macKey, iv || ephemPublicKey || ciphertext)\n *\n * @category Cryptography\n */\nexport abstract class BaseECIESUint8 implements ECIESProvider {\n // Cache for validated public keys to avoid repeated validation\n private static readonly validatedKeys = new WeakMap<Uint8Array, boolean>();\n\n /**\n * Generates cryptographically secure random bytes.\n *\n * @param length - Number of random bytes to generate.\n * @returns Random bytes array.\n */\n protected abstract generateRandomBytes(length: number): Uint8Array;\n\n /**\n * Verifies a private key is valid for secp256k1.\n *\n * @param privateKey - Private key to verify (32 bytes).\n * @returns `true` if valid private key.\n */\n protected abstract verifyPrivateKey(privateKey: Uint8Array): boolean;\n\n /**\n * Creates a public key from a private key.\n *\n * @param privateKey - Source private key (32 bytes).\n * @param compressed - Generate compressed (33B) or uncompressed (65B) format.\n * @returns Public key or `null` if creation failed.\n */\n protected abstract createPublicKey(\n privateKey: Uint8Array,\n compressed: boolean,\n ): Uint8Array | null;\n\n /**\n * Validates a public key on the secp256k1 curve.\n *\n * @param publicKey - Public key to validate.\n * @returns `true` if valid public key.\n */\n protected abstract validatePublicKey(publicKey: Uint8Array): boolean;\n\n /**\n * Decompresses a compressed public key.\n *\n * @param publicKey - Compressed public key (33 bytes).\n * @returns Uncompressed public key (65 bytes) or `null` if decompression failed.\n */\n protected abstract decompressPublicKey(\n publicKey: Uint8Array,\n ): Uint8Array | null;\n\n /**\n * Performs ECDH key agreement.\n *\n * @param publicKey - Other party's public key.\n * @param privateKey - Your private key.\n * @returns Raw X coordinate of shared point (32 bytes).\n */\n protected abstract performECDH(\n publicKey: Uint8Array,\n privateKey: Uint8Array,\n ): Uint8Array;\n\n /**\n * Computes SHA-512 hash.\n *\n * @param data - Data to hash.\n * @returns SHA-512 hash (64 bytes).\n */\n protected abstract sha512(data: Uint8Array): Uint8Array;\n\n /**\n * Computes HMAC-SHA256 authentication tag.\n *\n * @param key - HMAC key.\n * @param data - Data to authenticate.\n * @returns HMAC-SHA256 (32 bytes).\n */\n protected abstract hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array;\n\n /**\n * Encrypts data using AES-256-CBC.\n *\n * @param key - Encryption key (32 bytes).\n * @param iv - Initialization vector (16 bytes).\n * @param plaintext - Data to encrypt.\n * @returns Ciphertext with PKCS#7 padding.\n */\n protected abstract aesEncrypt(\n key: Uint8Array,\n iv: Uint8Array,\n plaintext: Uint8Array,\n ): Promise<Uint8Array>;\n\n /**\n * Decrypts data using AES-256-CBC.\n *\n * @param key - Decryption key (32 bytes).\n * @param iv - Initialization vector (16 bytes).\n * @param ciphertext - Data to decrypt.\n * @returns Plaintext with padding removed.\n */\n protected abstract aesDecrypt(\n key: Uint8Array,\n iv: Uint8Array,\n ciphertext: Uint8Array,\n ): Promise<Uint8Array>;\n\n /**\n * Normalizes a public key to uncompressed format.\n *\n * @param publicKey - Public key in any format.\n * @returns Uncompressed public key (65 bytes).\n * @throws {ECIESError} If key format is invalid.\n */\n protected normalizePublicKey(publicKey: Uint8Array): Uint8Array {\n // Check cache first\n if (BaseECIESUint8.validatedKeys.get(publicKey)) {\n return publicKey;\n }\n\n if (publicKey.length === CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH) {\n if (publicKey[0] !== CURVE.PREFIX.UNCOMPRESSED) {\n throw new ECIESError(\n \"Invalid uncompressed public key prefix\",\n \"INVALID_KEY\",\n );\n }\n // Validate and cache\n if (!this.validatePublicKey(publicKey)) {\n throw new ECIESError(\"Invalid public key\", \"INVALID_KEY\");\n }\n BaseECIESUint8.validatedKeys.set(publicKey, true);\n return publicKey;\n }\n\n if (publicKey.length === CURVE.COMPRESSED_PUBLIC_KEY_LENGTH) {\n if (\n publicKey[0] === CURVE.PREFIX.COMPRESSED_EVEN ||\n publicKey[0] === CURVE.PREFIX.COMPRESSED_ODD\n ) {\n const decompressed = this.decompressPublicKey(publicKey);\n if (!decompressed) {\n throw new ECIESError(\n \"Failed to decompress public key\",\n \"INVALID_KEY\",\n );\n }\n // Cache the decompressed key\n BaseECIESUint8.validatedKeys.set(decompressed, true);\n return decompressed;\n }\n throw new ECIESError(\n `Invalid compressed public key prefix: expected 0x02 or 0x03, got 0x${publicKey[0].toString(16).padStart(2, \"0\")}`,\n \"INVALID_KEY\",\n );\n }\n\n throw new ECIESError(\n `Invalid public key length: ${publicKey.length}`,\n \"INVALID_KEY\",\n );\n }\n\n /**\n * Normalizes a public key to uncompressed format (65 bytes with 0x04 prefix).\n * Must be implemented by derived classes to handle platform-specific operations.\n *\n * @param publicKey - The public key to normalize\n * @returns The normalized uncompressed public key\n */\n public abstract normalizeToUncompressed(publicKey: Uint8Array): Uint8Array;\n\n /**\n * Encrypts data using ECIES.\n *\n * @param publicKey - The recipient's public key (compressed or uncompressed)\n * @param message - The data to encrypt\n * @returns Promise resolving to encrypted data structure\n */\n async encrypt(\n publicKey: Uint8Array,\n message: Uint8Array,\n ): Promise<ECIESEncrypted> {\n // Declare sensitive variables outside try so finally can access them\n let ephemeralPrivateKey: Uint8Array | undefined;\n let sharedSecret: Uint8Array | undefined;\n let kdf: Uint8Array | undefined;\n let encryptionKey: Uint8Array | undefined;\n let macKey: Uint8Array | undefined;\n\n try {\n // Validate inputs\n if (!(publicKey instanceof Uint8Array)) {\n throw new ECIESError(\"Public key must be a Uint8Array\", \"INVALID_KEY\");\n }\n if (!(message instanceof Uint8Array)) {\n throw new ECIESError(\n \"Message must be a Uint8Array\",\n \"ENCRYPTION_FAILED\",\n );\n }\n if (publicKey.length === 0) {\n throw new ECIESError(\"Public key cannot be empty\", \"INVALID_KEY\");\n }\n\n // Normalize public key to uncompressed format\n const pubKey = this.normalizePublicKey(publicKey);\n\n // Generate ephemeral key pair\n do {\n ephemeralPrivateKey = this.generateRandomBytes(\n CURVE.PRIVATE_KEY_LENGTH,\n );\n } while (!this.verifyPrivateKey(ephemeralPrivateKey));\n\n const ephemeralPublicKey = this.createPublicKey(\n ephemeralPrivateKey,\n false,\n );\n if (!ephemeralPublicKey) {\n throw new ECIESError(\n \"Failed to generate ephemeral public key\",\n \"ENCRYPTION_FAILED\",\n );\n }\n\n // Perform ECDH to get shared secret (raw X coordinate)\n sharedSecret = this.performECDH(pubKey, ephemeralPrivateKey);\n\n // Derive keys using SHA-512 (eccrypto-compatible KDF)\n kdf = this.sha512(sharedSecret);\n encryptionKey = kdf.slice(\n KDF.ENCRYPTION_KEY_OFFSET,\n KDF.ENCRYPTION_KEY_OFFSET + KDF.ENCRYPTION_KEY_LENGTH,\n );\n macKey = kdf.slice(\n KDF.MAC_KEY_OFFSET,\n KDF.MAC_KEY_OFFSET + KDF.MAC_KEY_LENGTH,\n );\n\n // Generate random IV and encrypt\n const iv = this.generateRandomBytes(CIPHER.IV_LENGTH);\n const ciphertext = await this.aesEncrypt(encryptionKey, iv, message);\n\n // Calculate MAC (Encrypt-then-MAC)\n const macData = concat([iv, ephemeralPublicKey, ciphertext]);\n const mac = this.hmacSha256(macKey, macData);\n\n return {\n iv,\n ephemPublicKey: ephemeralPublicKey,\n ciphertext,\n mac,\n };\n } catch (error) {\n if (error instanceof ECIESError) throw error;\n throw new ECIESError(\n `Encryption failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"ENCRYPTION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n } finally {\n // Clear sensitive data on all code paths (success, error, throw)\n if (ephemeralPrivateKey) this.clearBuffer(ephemeralPrivateKey);\n if (sharedSecret) this.clearBuffer(sharedSecret);\n if (kdf) this.clearBuffer(kdf);\n if (encryptionKey) this.clearBuffer(encryptionKey);\n if (macKey) this.clearBuffer(macKey);\n }\n }\n\n /**\n * Decrypts ECIES encrypted data.\n *\n * @param privateKey - The recipient's private key (32 bytes)\n * @param encrypted - The encrypted data structure from encrypt()\n * @returns Promise resolving to the original plaintext\n */\n async decrypt(\n privateKey: Uint8Array,\n encrypted: ECIESEncrypted,\n ): Promise<Uint8Array> {\n // Declare sensitive variables outside try so finally can access them\n let sharedSecret: Uint8Array | undefined;\n let kdf: Uint8Array | undefined;\n let encryptionKey: Uint8Array | undefined;\n let macKey: Uint8Array | undefined;\n\n try {\n // Validate inputs\n if (!(privateKey instanceof Uint8Array)) {\n throw new ECIESError(\"Private key must be a Uint8Array\", \"INVALID_KEY\");\n }\n if (!isECIESEncrypted(encrypted)) {\n throw new ECIESError(\n \"Invalid encrypted data structure\",\n \"DECRYPTION_FAILED\",\n );\n }\n if (privateKey.length !== CURVE.PRIVATE_KEY_LENGTH) {\n throw new ECIESError(\n `Invalid private key length: ${privateKey.length}`,\n \"INVALID_KEY\",\n );\n }\n if (!this.verifyPrivateKey(privateKey)) {\n throw new ECIESError(\"Invalid private key\", \"INVALID_KEY\");\n }\n\n // Strict validation: ephemeral keys must be 65-byte uncompressed (eccrypto standard)\n if (\n encrypted.ephemPublicKey.length !== CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH\n ) {\n throw new ECIESError(\n `Invalid ephemeral public key: expected ${CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH} bytes (uncompressed), got ${encrypted.ephemPublicKey.length} bytes`,\n \"INVALID_KEY\",\n );\n }\n if (encrypted.ephemPublicKey[0] !== CURVE.PREFIX.UNCOMPRESSED) {\n throw new ECIESError(\n \"Invalid ephemeral public key: must be uncompressed format with 0x04 prefix (eccrypto standard)\",\n \"INVALID_KEY\",\n );\n }\n if (!this.validatePublicKey(encrypted.ephemPublicKey)) {\n throw new ECIESError(\"Invalid ephemeral public key\", \"INVALID_KEY\");\n }\n const ephemeralPublicKey = encrypted.ephemPublicKey;\n\n // Perform ECDH to recover shared secret\n sharedSecret = this.performECDH(ephemeralPublicKey, privateKey);\n\n // Derive keys using SHA-512 (eccrypto-compatible KDF)\n kdf = this.sha512(sharedSecret);\n encryptionKey = kdf.slice(\n KDF.ENCRYPTION_KEY_OFFSET,\n KDF.ENCRYPTION_KEY_OFFSET + KDF.ENCRYPTION_KEY_LENGTH,\n );\n macKey = kdf.slice(\n KDF.MAC_KEY_OFFSET,\n KDF.MAC_KEY_OFFSET + KDF.MAC_KEY_LENGTH,\n );\n\n // Verify MAC before decryption (Encrypt-then-MAC)\n const macData = concat([\n encrypted.iv,\n encrypted.ephemPublicKey,\n encrypted.ciphertext,\n ]);\n const expectedMac = this.hmacSha256(macKey, macData);\n\n if (!constantTimeEqual(encrypted.mac, expectedMac)) {\n throw new ECIESError(\"MAC verification failed\", \"MAC_MISMATCH\");\n }\n\n // Decrypt the ciphertext\n const decrypted = await this.aesDecrypt(\n encryptionKey,\n encrypted.iv,\n encrypted.ciphertext,\n );\n\n return decrypted;\n } catch (error) {\n if (error instanceof ECIESError) throw error;\n throw new ECIESError(\n `Decryption failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n \"DECRYPTION_FAILED\",\n error instanceof Error ? error : undefined,\n );\n } finally {\n // Clear sensitive data on all code paths (success, error, throw)\n if (sharedSecret) this.clearBuffer(sharedSecret);\n if (kdf) this.clearBuffer(kdf);\n if (encryptionKey) this.clearBuffer(encryptionKey);\n if (macKey) this.clearBuffer(macKey);\n }\n }\n\n /**\n * Clears sensitive data from memory using multi-pass overwrite.\n *\n * @remarks\n * Uses multiple passes with different patterns to make it harder\n * for JIT compilers to optimize away the operation. While not\n * guaranteed in JavaScript, this is a best-effort approach to\n * clear sensitive data from memory.\n *\n * @param buffer - The buffer to clear\n */\n protected clearBuffer(buffer: Uint8Array): void {\n if (buffer && buffer.length > 0) {\n // Multi-pass overwrite to resist compiler optimization\n buffer.fill(0x00); // Fill with zeros\n buffer.fill(0xff); // Fill with ones\n buffer.fill(0xaa); // Fill with alternating pattern\n buffer.fill(0x00); // Final zero fill\n\n // Additional pattern write to further discourage optimization\n for (let i = 0; i < buffer.length; i++) {\n buffer[i] = (i & 0xff) ^ 0x5a; // XOR with pattern\n }\n buffer.fill(0x00); // Final clear\n }\n }\n}\n", "/**\n * Browser implementation of ECIES using @noble/secp256k1 with Uint8Array\n *\n * @remarks\n * Uses native browser crypto APIs and @noble/secp256k1 for elliptic curve operations.\n * This implementation is polyfill-free and works in all modern browsers.\n */\n\nimport * as secp256k1 from \"@noble/secp256k1\";\nimport { BaseECIESUint8 } from \"./base\";\nimport { toHex } from \"viem\";\nimport { hmac } from \"@noble/hashes/hmac\";\nimport { sha256, sha512 as nobleSha512 } from \"@noble/hashes/sha2\";\n\n/**\n * Browser-specific ECIES provider using @noble/secp256k1\n *\n * @remarks\n * This implementation uses:\n * - Web Crypto API for AES operations\n * - @noble/secp256k1 for elliptic curve operations\n * - @noble/hashes for SHA and HMAC operations\n * - No Buffer or Node.js dependencies\n */\nexport class BrowserECIESUint8Provider extends BaseECIESUint8