UNPKG

@opendatalabs/vana-sdk

Version:

A TypeScript library for interacting with Vana Network smart contracts.

268 lines 10.2 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var base_exports = {}; __export(base_exports, { BaseECIESUint8: () => BaseECIESUint8 }); module.exports = __toCommonJS(base_exports); var import_interface = require("./interface"); var import_constants = require("./constants"); var import_utils = require("./utils"); var import_viem = require("viem"); class BaseECIESUint8 { // Cache for validated public keys to avoid repeated validation static validatedKeys = /* @__PURE__ */ new WeakMap(); /** * Normalizes a public key to uncompressed format. * * @param publicKey - Public key in any format. * @returns Uncompressed public key (65 bytes). * @throws {ECIESError} If key format is invalid. */ normalizePublicKey(publicKey) { if (BaseECIESUint8.validatedKeys.get(publicKey)) { return publicKey; } if (publicKey.length === import_constants.CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH) { if (publicKey[0] !== import_constants.CURVE.PREFIX.UNCOMPRESSED) { throw new import_interface.ECIESError( "Invalid uncompressed public key prefix", "INVALID_KEY" ); } if (!this.validatePublicKey(publicKey)) { throw new import_interface.ECIESError("Invalid public key", "INVALID_KEY"); } BaseECIESUint8.validatedKeys.set(publicKey, true); return publicKey; } if (publicKey.length === import_constants.CURVE.COMPRESSED_PUBLIC_KEY_LENGTH) { if (publicKey[0] === import_constants.CURVE.PREFIX.COMPRESSED_EVEN || publicKey[0] === import_constants.CURVE.PREFIX.COMPRESSED_ODD) { const decompressed = this.decompressPublicKey(publicKey); if (!decompressed) { throw new import_interface.ECIESError( "Failed to decompress public key", "INVALID_KEY" ); } BaseECIESUint8.validatedKeys.set(decompressed, true); return decompressed; } throw new import_interface.ECIESError( `Invalid compressed public key prefix: expected 0x02 or 0x03, got 0x${publicKey[0].toString(16).padStart(2, "0")}`, "INVALID_KEY" ); } throw new import_interface.ECIESError( `Invalid public key length: ${publicKey.length}`, "INVALID_KEY" ); } /** * Encrypts data using ECIES. * * @param publicKey - The recipient's public key (compressed or uncompressed) * @param message - The data to encrypt * @returns Promise resolving to encrypted data structure */ async encrypt(publicKey, message) { let ephemeralPrivateKey; let sharedSecret; let kdf; let encryptionKey; let macKey; try { if (!(publicKey instanceof Uint8Array)) { throw new import_interface.ECIESError("Public key must be a Uint8Array", "INVALID_KEY"); } if (!(message instanceof Uint8Array)) { throw new import_interface.ECIESError( "Message must be a Uint8Array", "ENCRYPTION_FAILED" ); } if (publicKey.length === 0) { throw new import_interface.ECIESError("Public key cannot be empty", "INVALID_KEY"); } const pubKey = this.normalizePublicKey(publicKey); do { ephemeralPrivateKey = this.generateRandomBytes( import_constants.CURVE.PRIVATE_KEY_LENGTH ); } while (!this.verifyPrivateKey(ephemeralPrivateKey)); const ephemeralPublicKey = this.createPublicKey( ephemeralPrivateKey, false ); if (!ephemeralPublicKey) { throw new import_interface.ECIESError( "Failed to generate ephemeral public key", "ENCRYPTION_FAILED" ); } sharedSecret = this.performECDH(pubKey, ephemeralPrivateKey); kdf = this.sha512(sharedSecret); encryptionKey = kdf.slice( import_constants.KDF.ENCRYPTION_KEY_OFFSET, import_constants.KDF.ENCRYPTION_KEY_OFFSET + import_constants.KDF.ENCRYPTION_KEY_LENGTH ); macKey = kdf.slice( import_constants.KDF.MAC_KEY_OFFSET, import_constants.KDF.MAC_KEY_OFFSET + import_constants.KDF.MAC_KEY_LENGTH ); const iv = this.generateRandomBytes(import_constants.CIPHER.IV_LENGTH); const ciphertext = await this.aesEncrypt(encryptionKey, iv, message); const macData = (0, import_viem.concat)([iv, ephemeralPublicKey, ciphertext]); const mac = this.hmacSha256(macKey, macData); return { iv, ephemPublicKey: ephemeralPublicKey, ciphertext, mac }; } catch (error) { if (error instanceof import_interface.ECIESError) throw error; throw new import_interface.ECIESError( `Encryption failed: ${error instanceof Error ? error.message : "Unknown error"}`, "ENCRYPTION_FAILED", error instanceof Error ? error : void 0 ); } finally { if (ephemeralPrivateKey) this.clearBuffer(ephemeralPrivateKey); if (sharedSecret) this.clearBuffer(sharedSecret); if (kdf) this.clearBuffer(kdf); if (encryptionKey) this.clearBuffer(encryptionKey); if (macKey) this.clearBuffer(macKey); } } /** * Decrypts ECIES encrypted data. * * @param privateKey - The recipient's private key (32 bytes) * @param encrypted - The encrypted data structure from encrypt() * @returns Promise resolving to the original plaintext */ async decrypt(privateKey, encrypted) { let sharedSecret; let kdf; let encryptionKey; let macKey; try { if (!(privateKey instanceof Uint8Array)) { throw new import_interface.ECIESError("Private key must be a Uint8Array", "INVALID_KEY"); } if (!(0, import_interface.isECIESEncrypted)(encrypted)) { throw new import_interface.ECIESError( "Invalid encrypted data structure", "DECRYPTION_FAILED" ); } if (privateKey.length !== import_constants.CURVE.PRIVATE_KEY_LENGTH) { throw new import_interface.ECIESError( `Invalid private key length: ${privateKey.length}`, "INVALID_KEY" ); } if (!this.verifyPrivateKey(privateKey)) { throw new import_interface.ECIESError("Invalid private key", "INVALID_KEY"); } if (encrypted.ephemPublicKey.length !== import_constants.CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH) { throw new import_interface.ECIESError( `Invalid ephemeral public key: expected ${import_constants.CURVE.UNCOMPRESSED_PUBLIC_KEY_LENGTH} bytes (uncompressed), got ${encrypted.ephemPublicKey.length} bytes`, "INVALID_KEY" ); } if (encrypted.ephemPublicKey[0] !== import_constants.CURVE.PREFIX.UNCOMPRESSED) { throw new import_interface.ECIESError( "Invalid ephemeral public key: must be uncompressed format with 0x04 prefix (eccrypto standard)", "INVALID_KEY" ); } if (!this.validatePublicKey(encrypted.ephemPublicKey)) { throw new import_interface.ECIESError("Invalid ephemeral public key", "INVALID_KEY"); } const ephemeralPublicKey = encrypted.ephemPublicKey; sharedSecret = this.performECDH(ephemeralPublicKey, privateKey); kdf = this.sha512(sharedSecret); encryptionKey = kdf.slice( import_constants.KDF.ENCRYPTION_KEY_OFFSET, import_constants.KDF.ENCRYPTION_KEY_OFFSET + import_constants.KDF.ENCRYPTION_KEY_LENGTH ); macKey = kdf.slice( import_constants.KDF.MAC_KEY_OFFSET, import_constants.KDF.MAC_KEY_OFFSET + import_constants.KDF.MAC_KEY_LENGTH ); const macData = (0, import_viem.concat)([ encrypted.iv, encrypted.ephemPublicKey, encrypted.ciphertext ]); const expectedMac = this.hmacSha256(macKey, macData); if (!(0, import_utils.constantTimeEqual)(encrypted.mac, expectedMac)) { throw new import_interface.ECIESError("MAC verification failed", "MAC_MISMATCH"); } const decrypted = await this.aesDecrypt( encryptionKey, encrypted.iv, encrypted.ciphertext ); return decrypted; } catch (error) { if (error instanceof import_interface.ECIESError) throw error; throw new import_interface.ECIESError( `Decryption failed: ${error instanceof Error ? error.message : "Unknown error"}`, "DECRYPTION_FAILED", error instanceof Error ? error : void 0 ); } finally { if (sharedSecret) this.clearBuffer(sharedSecret); if (kdf) this.clearBuffer(kdf); if (encryptionKey) this.clearBuffer(encryptionKey); if (macKey) this.clearBuffer(macKey); } } /** * Clears sensitive data from memory using multi-pass overwrite. * * @remarks * Uses multiple passes with different patterns to make it harder * for JIT compilers to optimize away the operation. While not * guaranteed in JavaScript, this is a best-effort approach to * clear sensitive data from memory. * * @param buffer - The buffer to clear */ clearBuffer(buffer) { if (buffer && buffer.length > 0) { buffer.fill(0); buffer.fill(255); buffer.fill(170); buffer.fill(0); for (let i = 0; i < buffer.length; i++) { buffer[i] = i & 255 ^ 90; } buffer.fill(0); } } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { BaseECIESUint8 }); //# sourceMappingURL=base.cjs.map