import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { execFileSync, execSync } from "node:child_process";
//#region extensions/crypto/src/services/keychain-wallet.ts
/**
* Keychain Wallet — local key generation with encrypted Keychain storage.
*
* Extends `private_key` mode with:
* - BIP-39 mnemonic generation via viem/accounts
* - scrypt + AES-256-GCM envelope encryption
* - macOS Keychain storage (via `security` CLI)
* - Encrypted file fallback for Linux/Docker
*
* The runtime behavior is identical to raw `private_key` mode — once we have
* a viem Account object, the same wallet client code path is used. The only
* difference is how the key is acquired (generated vs env var) and stored
* (Keychain vs plaintext env).
*
* No new dependencies. Uses:
* - viem/accounts: generateMnemonic, mnemonicToAccount
* - @scure/bip39/wordlists/english: BIP-39 English wordlist (transitive via viem)
* - @noble/hashes/scrypt: key derivation
* - node:crypto: AES-256-GCM encryption
*/
const KEYCHAIN_ACCOUNT = "eth";
const KEYCHAIN_SERVICE = "clawncher_mnemonic";
const FALLBACK_DIR = join(process.env.HOME ?? "/root", ".openclawnch");
const FALLBACK_PATH = join(FALLBACK_DIR, "wallet.enc");
const BACKUP_FILENAME = "wallet-backup.enc";
/** scrypt parameters: N=2^17, r=8, p=1. ~128MB memory, ~0.5s on modern hardware. */
const SCRYPT_N = 2 ** 17;
const SCRYPT_R = 8;
const SCRYPT_P = 1;
const SCRYPT_DKLEN = 32;
function isMacOS() {
	return process.platform === "darwin";
}
/**
* Derive an AES-256 key from a password using scrypt.
* Uses @noble/hashes for the scrypt implementation (already in dep tree via viem).
*/
async function deriveKey(password, salt) {
	const { scryptAsync } = await import("../../scrypt-DPe_rSTb.mjs");
	const derived = await scryptAsync(Buffer.from(password, "utf-8"), salt, {
		N: SCRYPT_N,
		r: SCRYPT_R,
		p: SCRYPT_P,
		dkLen: SCRYPT_DKLEN
	});
	return Buffer.from(derived);
}
/**
* Encrypt a plaintext string with AES-256-GCM using a password-derived key.
*/
async function encrypt(plaintext, password) {
	const salt = randomBytes(32);
	const nonce = randomBytes(12);
	const cipher = createCipheriv("aes-256-gcm", await deriveKey(password, salt), nonce);
	const encrypted = Buffer.concat([cipher.update(plaintext, "utf-8"), cipher.final()]);
	const tag = cipher.getAuthTag();
	return {
		salt: salt.toString("hex"),
		nonce: nonce.toString("hex"),
		ciphertext: encrypted.toString("hex"),
		tag: tag.toString("hex"),
		version: 1
	};
}
/**
* Decrypt an AES-256-GCM encrypted payload using a password-derived key.
* Throws on wrong password (GCM auth tag mismatch).
*/
async function decrypt(payload, password) {
	if (payload.version !== 1) throw new Error(`Unsupported encryption version: ${payload.version}`);
	const salt = Buffer.from(payload.salt, "hex");
	const nonce = Buffer.from(payload.nonce, "hex");
	const ciphertext = Buffer.from(payload.ciphertext, "hex");
	const tag = Buffer.from(payload.tag, "hex");
	const decipher = createDecipheriv("aes-256-gcm", await deriveKey(password, salt), nonce);
	decipher.setAuthTag(tag);
	try {
		return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf-8");
	} catch {
		throw new Error("Decryption failed — wrong password or corrupted data.");
	}
}
/**
* Store an encrypted payload in macOS Keychain.
*/
function keychainStore(payload) {
	const json = JSON.stringify(payload);
	try {
		execFileSync("security", [
			"delete-generic-password",
			"-a",
			KEYCHAIN_ACCOUNT,
			"-s",
			KEYCHAIN_SERVICE,
			"login.keychain-db"
		], { stdio: "pipe" });
	} catch {}
	execFileSync("security", [
		"add-generic-password",
		"-a",
		KEYCHAIN_ACCOUNT,
		"-s",
		KEYCHAIN_SERVICE,
		"-w",
		json,
		"login.keychain-db"
	], { stdio: "pipe" });
}
/**
* Load an encrypted payload from macOS Keychain.
* Returns null if not found.
*/
function keychainLoad() {
	try {
		const raw = execFileSync("security", [
			"find-generic-password",
			"-a",
			KEYCHAIN_ACCOUNT,
			"-s",
			KEYCHAIN_SERVICE,
			"-w",
			"login.keychain-db"
		], {
			encoding: "utf-8",
			stdio: [
				"pipe",
				"pipe",
				"pipe"
			]
		}).trim();
		return JSON.parse(raw);
	} catch {
		return null;
	}
}
/**
* Check if an encrypted wallet exists in macOS Keychain.
*/
function keychainExists() {
	try {
		execFileSync("security", [
			"find-generic-password",
			"-a",
			KEYCHAIN_ACCOUNT,
			"-s",
			KEYCHAIN_SERVICE,
			"login.keychain-db"
		], { stdio: "pipe" });
		return true;
	} catch {
		return false;
	}
}
/**
* Delete the encrypted wallet from macOS Keychain.
*/
function keychainDelete() {
	try {
		execSync(`security delete-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" login.keychain-db`, { stdio: "pipe" });
		return true;
	} catch {
		return false;
	}
}
function ensureFallbackDir() {
	if (!existsSync(FALLBACK_DIR)) mkdirSync(FALLBACK_DIR, {
		recursive: true,
		mode: 448
	});
}
function fileStore(payload) {
	ensureFallbackDir();
	writeFileSync(FALLBACK_PATH, JSON.stringify(payload), { mode: 384 });
}
function fileLoad() {
	try {
		const raw = readFileSync(FALLBACK_PATH, "utf-8");
		return JSON.parse(raw);
	} catch {
		return null;
	}
}
function fileExists() {
	return existsSync(FALLBACK_PATH);
}
function fileDelete() {
	try {
		unlinkSync(FALLBACK_PATH);
		return true;
	} catch {
		return false;
	}
}
function storePayload(payload) {
	if (isMacOS()) keychainStore(payload);
	else fileStore(payload);
}
function loadPayload() {
	if (isMacOS()) return keychainLoad();
	return fileLoad();
}
/**
* Generate a new BIP-39 wallet. Returns the mnemonic and derived address.
* Does NOT store anything — call encryptAndStore() after user confirms backup.
*/
async function generateWallet() {
	const { generateMnemonic, mnemonicToAccount } = await import("../../accounts-CgUUF-_i.mjs");
	const { wordlist } = await import("../../english-tut6fm4l.mjs");
	const mnemonic = generateMnemonic(wordlist);
	return {
		mnemonic,
		address: mnemonicToAccount(mnemonic).address
	};
}
/**
* Encrypt a mnemonic with user password and store in Keychain (macOS) or
* encrypted file (Linux/Docker).
*/
async function encryptAndStore(mnemonic, password) {
	const words = mnemonic.trim().split(/\s+/);
	if (words.length !== 12 && words.length !== 24) throw new Error(`Invalid mnemonic: expected 12 or 24 words, got ${words.length}`);
	if (!password || password.length < 8) throw new Error("Password must be at least 8 characters.");
	storePayload(await encrypt(mnemonic, password));
}
/**
* Load and decrypt the stored mnemonic, then derive a viem Account.
* Returns the Account object ready for privateKeyToAccount-style usage.
*/
async function loadAndDecrypt(password) {
	const payload = loadPayload();
	if (!payload) throw new Error("No encrypted wallet found in storage.");
	const mnemonic = await decrypt(payload, password);
	const { mnemonicToAccount } = await import("../../accounts-CgUUF-_i.mjs");
	const account = mnemonicToAccount(mnemonic);
	return {
		account,
		mnemonic,
		address: account.address
	};
}
/**
* Check if an encrypted wallet exists in storage.
*/
function hasKeychainWallet() {
	if (isMacOS()) return keychainExists();
	return fileExists();
}
/**
* Delete the encrypted wallet from storage.
*/
function deleteKeychainWallet() {
	if (isMacOS()) return keychainDelete();
	return fileDelete();
}
/**
* Export encrypted backup file to a specified path (or default backup location).
* Returns the path where the backup was written.
*/
function exportBackupFile(outputDir) {
	const payload = loadPayload();
	if (!payload) throw new Error("No encrypted wallet found in storage.");
	const dir = outputDir ?? FALLBACK_DIR;
	if (!existsSync(dir)) mkdirSync(dir, {
		recursive: true,
		mode: 448
	});
	const backupPath = join(dir, BACKUP_FILENAME);
	writeFileSync(backupPath, JSON.stringify(payload, null, 2), { mode: 384 });
	return backupPath;
}
/**
* Import wallet from a backup file. Requires the original password.
* Stores into the active storage backend (Keychain or file).
*/
async function importFromBackup(backupPath, password) {
	const raw = readFileSync(backupPath, "utf-8");
	const payload = JSON.parse(raw);
	const mnemonic = await decrypt(payload, password);
	const { mnemonicToAccount } = await import("../../accounts-CgUUF-_i.mjs");
	const account = mnemonicToAccount(mnemonic);
	storePayload(payload);
	return account.address;
}
/**
* Generate 3 random word indices for mnemonic confirmation.
* Returns array of {index, word} pairs the user must confirm.
*/
function getConfirmationWords(mnemonic, count = 3) {
	const words = mnemonic.trim().split(/\s+/);
	const indices = /* @__PURE__ */ new Set();
	while (indices.size < count) {
		const idx = Math.floor(Math.random() * words.length);
		indices.add(idx);
	}
	return Array.from(indices).sort((a, b) => a - b).map((idx) => ({
		index: idx + 1,
		word: words[idx]
	}));
}
/**
* Validate that the user's confirmation words match the mnemonic.
*/
function validateConfirmation(mnemonic, confirmations) {
	const words = mnemonic.trim().split(/\s+/);
	return confirmations.every(({ index, word }) => words[index - 1]?.toLowerCase() === word.toLowerCase());
}
/**
* Get the storage backend description (for display to user).
*/
function getStorageInfo() {
	if (isMacOS()) return { backend: "keychain" };
	return {
		backend: "file",
		path: FALLBACK_PATH
	};
}
//#endregion
export { decrypt, deleteKeychainWallet, encrypt, encryptAndStore, exportBackupFile, generateWallet, getConfirmationWords, getStorageInfo, hasKeychainWallet, importFromBackup, loadAndDecrypt, validateConfirmation };

//# sourceMappingURL=keychain-wallet.mjs.map