@opendatalabs/vana-sdk
Version:
A TypeScript library for interacting with Vana Network smart contracts.
1 lines • 8.31 kB
Source Map (JSON)
{"version":3,"sources":["../../../src/crypto/ecies/browser.ts"],"sourcesContent":["/**\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 {\n protected generateRandomBytes(length: number): Uint8Array {\n const bytes = new Uint8Array(length);\n crypto.getRandomValues(bytes);\n return bytes;\n }\n\n protected verifyPrivateKey(privateKey: Uint8Array): boolean {\n try {\n return secp256k1.utils.isValidPrivateKey(privateKey);\n } catch {\n return false;\n }\n }\n\n protected createPublicKey(\n privateKey: Uint8Array,\n compressed: boolean,\n ): Uint8Array | null {\n try {\n return secp256k1.getPublicKey(privateKey, compressed);\n } catch {\n return null;\n }\n }\n\n protected validatePublicKey(publicKey: Uint8Array): boolean {\n try {\n // @noble/secp256k1 will throw if the point is not on the curve\n secp256k1.Point.fromHex(publicKey);\n return true;\n } catch {\n return false;\n }\n }\n\n protected decompressPublicKey(publicKey: Uint8Array): Uint8Array | null {\n try {\n // @noble/secp256k1 handles both compressed and uncompressed\n const point = secp256k1.Point.fromHex(publicKey);\n return point.toRawBytes(false); // false = uncompressed\n } catch {\n return null;\n }\n }\n\n protected performECDH(\n publicKey: Uint8Array,\n privateKey: Uint8Array,\n ): Uint8Array {\n try {\n // Use @noble/secp256k1's getSharedSecret which is optimized and secure\n // The 'true' parameter returns the raw x-coordinate (32 bytes)\n // This matches eccrypto's behavior\n const sharedPoint = secp256k1.getSharedSecret(\n privateKey,\n publicKey,\n true,\n );\n\n // getSharedSecret returns compressed point (33 bytes) when true\n // We need just the x-coordinate (32 bytes) for eccrypto compatibility\n // Remove the prefix byte\n return sharedPoint.slice(1);\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 return nobleSha512(data);\n }\n\n protected hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array {\n return hmac(sha256, key, data);\n }\n\n protected async aesEncrypt(\n key: Uint8Array,\n iv: Uint8Array,\n plaintext: Uint8Array,\n ): Promise<Uint8Array> {\n // Import the key for AES-CBC\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n key as BufferSource,\n { name: \"AES-CBC\" },\n false,\n [\"encrypt\"],\n );\n\n // Encrypt with Web Crypto API\n // Note: Web Crypto API automatically handles PKCS#7 padding for AES-CBC\n const encrypted = await crypto.subtle.encrypt(\n { name: \"AES-CBC\", iv: iv as BufferSource },\n cryptoKey,\n plaintext as BufferSource,\n );\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 // Import the key for AES-CBC\n const cryptoKey = await crypto.subtle.importKey(\n \"raw\",\n key as BufferSource,\n { name: \"AES-CBC\" },\n false,\n [\"decrypt\"],\n );\n\n // Decrypt with Web Crypto API\n // Note: Web Crypto API automatically handles PKCS#7 padding removal\n const decrypted = await crypto.subtle.decrypt(\n { name: \"AES-CBC\", iv: iv as BufferSource },\n cryptoKey,\n ciphertext as BufferSource,\n );\n\n return new Uint8Array(decrypted);\n }\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 @noble/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":"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","names":["nobleSha512"]}