import {useEffect, useRef, useState} from "react"; import {Copy, Loader2, LockKeyhole, UnlockKeyhole} from "lucide-react"; import { decryptKmsResource, encryptKmsResource, type KmsEncryptionAlgorithm, } from "@/api/cloudProxyClient"; import {useAccountId} from "@/lib/accountStore"; import type {CloudProvider} from "@/types/cloud"; import type {CloudResource} from "@/types/resource"; import {ServiceIcon} from "@/components/serviceIcons"; interface KmsCryptoPanelProps { cloud: CloudProvider; resource?: CloudResource; runtimeReachable: boolean; } type CryptoMode = "encrypt" | "decrypt"; type CryptoResult = {label: string; value: string}; export function KmsCryptoPanel({cloud, resource, runtimeReachable}: KmsCryptoPanelProps) { const accountId = useAccountId(); const keySpec = metadataString(resource, "keySpec"); const keyUsage = metadataString(resource, "keyUsage"); const keyEnabled = resource?.status === "Enabled" && resource.metadata.enabled === true; const [mode, setMode] = useState("encrypt"); const [plaintext, setPlaintext] = useState(""); const [ciphertext, setCiphertext] = useState(""); const [algorithm, setAlgorithm] = useState(defaultAlgorithm(keySpec)); const [contextText, setContextText] = useState(""); const [result, setResult] = useState(null); const [error, setError] = useState(null); const [pending, setPending] = useState(false); const [copied, setCopied] = useState(false); const activeRequest = useRef(null); useEffect(() => { activeRequest.current?.abort(); activeRequest.current = null; setMode("encrypt"); setPlaintext(""); setCiphertext(""); setAlgorithm(defaultAlgorithm(keySpec)); setContextText(""); setResult(null); setError(null); setPending(false); setCopied(false); return () => { activeRequest.current?.abort(); activeRequest.current = null; }; }, [accountId, resource?.id, keySpec]); const isKmsKey = resource?.service === "kms" && resource.type === "key"; const isSupportedKey = keyUsage === "ENCRYPT_DECRYPT" && keyEnabled && isEncryptKeySpec(keySpec); const canSubmit = Boolean(isKmsKey && isSupportedKey && runtimeReachable && !pending); const isRsa = keySpec === "RSA_2048" || keySpec === "RSA_4096"; const changeMode = (next: CryptoMode) => { if (next === mode) return; activeRequest.current?.abort(); activeRequest.current = null; setMode(next); setPlaintext(""); setCiphertext(""); setContextText(""); setResult(null); setError(null); setPending(false); setCopied(false); }; const submit = async () => { if (!resource || !canSubmit) return; setError(null); setResult(null); setCopied(false); activeRequest.current?.abort(); const controller = new AbortController(); activeRequest.current = controller; setPending(true); try { const encryptionContext = isRsa ? undefined : parseEncryptionContext(contextText); if (mode === "encrypt") { if (!plaintext) throw new Error("Enter plaintext to encrypt."); const response = await encryptKmsResource(cloud, resource.id, { plaintextBase64: utf8ToBase64(plaintext), encryptionAlgorithm: algorithm, ...(encryptionContext ? {encryptionContext} : {}), }, controller.signal); if (controller.signal.aborted) return; setResult({label: "Ciphertext (Base64)", value: response.ciphertextBlobBase64}); } else { if (!ciphertext.trim()) throw new Error("Enter Base64 ciphertext to decrypt."); const response = await decryptKmsResource(cloud, resource.id, { ciphertextBlobBase64: ciphertext.trim(), encryptionAlgorithm: algorithm, ...(encryptionContext ? {encryptionContext} : {}), }, controller.signal); if (controller.signal.aborted) return; setResult(decryptedResult(response.plaintextBase64)); } } catch (submitError) { if (controller.signal.aborted) return; setError(submitError instanceof Error ? submitError.message : "KMS operation failed."); } finally { if (activeRequest.current === controller) { activeRequest.current = null; setPending(false); } } }; const copyResult = async () => { if (!result) return; await navigator.clipboard.writeText(result.value); setCopied(true); window.setTimeout(() => setCopied(false), 1200); }; if (!resource || resource.service !== "kms") { return (

Select an encryption key

Select an enabled ENCRYPT_DECRYPT KMS key to encrypt or decrypt data.

); } return (

KMS Actions

Encrypt and decrypt

Values stay in this panel and are cleared when you switch keys or operations.

{canSubmit ? "Ready" : operationUnavailableReason(keyUsage, keyEnabled, keySpec, runtimeReachable)}