import { createCipheriv, createDecipheriv, randomBytes, randomUUID, scryptSync, timingSafeEqual, } from "node:crypto";
import { readFileSync } from "node:fs";
import { hashTypedData, keccak256, } from "viem";
import { generatePrivateKey, mnemonicToAccount, privateKeyToAccount } from "viem/accounts";
// scrypt KDF parameters for keystore encryption (geth keystore v3 compatible).
const SCRYPT_N = 1 << 18; // 262144 — OWASP-recommended minimum for keystores
const SCRYPT_R = 8;
const SCRYPT_P = 1;
const SCRYPT_DKLEN = 32;
const SCRYPT_MAXMEM = 512 * 1024 * 1024; // 512 MB — N=262144,r=8 requires ~256 MB
/**
 * A locally-held signing key used by agents to authorize dispatches.
 * The owner key never belongs here — it stays in the user's wallet (MetaMask / WalletConnect).
 */
export class LocalKeyring {
    address;
    account;
    /** Present only when the keyring was constructed from a raw private key. */
    privateKey;
    constructor(options) {
        if (options.type === "privateKey") {
            const account = privateKeyToAccount(options.privateKey);
            this.account = account;
            this.address = account.address;
            this.privateKey = options.privateKey;
        }
        else if (options.type === "mnemonic") {
            const account = mnemonicToAccount(options.mnemonic, options.derivationPath
                ? { path: options.derivationPath }
                : undefined);
            this.account = account;
            this.address = account.address;
        }
        else {
            throw new Error("keystore decryption not implemented — use type: 'privateKey' or type: 'mnemonic' instead");
        }
    }
    /** Returns a lightweight signer stub for read-only contexts where the key is not available. */
    static fromAddress(address) {
        return { address };
    }
    /** Generates a brand-new keyring backed by a random private key. */
    static generate() {
        return new LocalKeyring({ type: "privateKey", privateKey: generatePrivateKey() });
    }
    /** Constructs a keyring from an existing raw private key. */
    static fromPrivateKey(privateKey) {
        return new LocalKeyring({ type: "privateKey", privateKey });
    }
    /**
     * The underlying viem account, for building a WalletClient
     * (e.g. `createWalletClient({ account: keyring.viemAccount, ... })`).
     */
    get viemAccount() {
        return this.account;
    }
    /** Decrypts an in-memory keystore object with the given password. */
    static async fromKeystore(keystore, password) {
        const crypto = keystore.crypto;
        if (crypto.kdf !== "scrypt") {
            throw new Error(`Unsupported keystore KDF: ${crypto.kdf}`);
        }
        const { n, r, p, dklen, salt } = crypto.kdfparams;
        // Enforce minimum work factor. An externally-supplied keystore with N=1 would
        // decrypt without error but be trivially brute-forceable.
        if (n < 1 << 14) {
            throw new Error(`Keystore scrypt N=${n} is below the minimum accepted value (16384). Refusing to decrypt.`);
        }
        if (r < 8) {
            throw new Error(`Keystore scrypt r=${r} is below the minimum accepted value (8).`);
        }
        const derived = scryptSync(password, Buffer.from(salt, "hex"), dklen, {
            N: n,
            r,
            p,
            maxmem: SCRYPT_MAXMEM,
        });
        const ciphertext = Buffer.from(crypto.ciphertext, "hex");
        const macInput = `0x${Buffer.concat([derived.subarray(16, 32), ciphertext]).toString("hex")}`;
        const computedMac = Buffer.from(keccak256(macInput).slice(2), "hex");
        const storedMac = Buffer.from(crypto.mac.toLowerCase(), "hex");
        if (computedMac.length !== storedMac.length || !timingSafeEqual(computedMac, storedMac)) {
            throw new Error("Invalid password or corrupt keystore");
        }
        const decipher = createDecipheriv("aes-128-ctr", derived.subarray(0, 16), Buffer.from(crypto.cipherparams.iv, "hex"));
        const pkBytes = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
        return LocalKeyring.fromPrivateKey(`0x${pkBytes.toString("hex")}`);
    }
    /** Loads a keyring from an encrypted JSON keystore file on disk. */
    static async fromKeystoreFile(path, password) {
        const keystore = JSON.parse(readFileSync(path, "utf-8"));
        return LocalKeyring.fromKeystore(keystore, password);
    }
    /** Signs a raw 32-byte hash. Returns a 65-byte ECDSA signature. */
    async sign(hash) {
        return this.signHash(hash);
    }
    /** Internal raw-hash signer. `sign` is optional on viem's LocalAccount but is
     *  always present for privateKey/mnemonic accounts. */
    signHash(hash) {
        if (!this.account.sign) {
            throw new Error("This keyring cannot sign raw hashes.");
        }
        return this.account.sign({ hash });
    }
    /**
     * Signs a typed-data payload (EIP-712).
     * `types` must be `{ primaryType: string; types: Record<string, TypedDataParameter[]> }`.
     */
    async signTyped(domain, types, value) {
        const { primaryType, types: typeDefs } = types;
        const hash = hashTypedData({
            domain: domain,
            types: typeDefs,
            primaryType,
            message: value,
        });
        return this.signHash(hash);
    }
    /** Exports the private key as an encrypted keystore JSON (scrypt + aes-128-ctr, geth v3). */
    async exportKeystore(password) {
        if (!this.privateKey) {
            throw new Error("Private key unavailable — only privateKey/generated keyrings can be exported");
        }
        const salt = randomBytes(32);
        const derived = scryptSync(password, salt, SCRYPT_DKLEN, {
            N: SCRYPT_N,
            r: SCRYPT_R,
            p: SCRYPT_P,
            maxmem: SCRYPT_MAXMEM,
        });
        const iv = randomBytes(16);
        const cipher = createCipheriv("aes-128-ctr", derived.subarray(0, 16), iv);
        const pkBytes = Buffer.from(this.privateKey.slice(2), "hex");
        const ciphertext = Buffer.concat([cipher.update(pkBytes), cipher.final()]);
        const macInput = `0x${Buffer.concat([derived.subarray(16, 32), ciphertext]).toString("hex")}`;
        const mac = keccak256(macInput).slice(2);
        return {
            version: 3,
            id: randomUUID(),
            address: this.address.slice(2).toLowerCase(),
            crypto: {
                cipher: "aes-128-ctr",
                ciphertext: ciphertext.toString("hex"),
                cipherparams: { iv: iv.toString("hex") },
                kdf: "scrypt",
                kdfparams: {
                    n: SCRYPT_N,
                    r: SCRYPT_R,
                    p: SCRYPT_P,
                    dklen: SCRYPT_DKLEN,
                    salt: salt.toString("hex"),
                },
                mac,
            },
        };
    }
}
//# sourceMappingURL=keyring.js.map