diff --git a/app/cipher-identifier/page.tsx b/app/cipher-identifier/page.tsx new file mode 100644 index 00000000..3a6d5958 --- /dev/null +++ b/app/cipher-identifier/page.tsx @@ -0,0 +1,22 @@ +import type { Metadata } from "next"; +import CipherIdentifier from "../../components/cryptanalysis/CipherIdentifier"; +import Navbar from "../../components/layout/Navbar"; +import Footer from "../../components/layout/footer"; + +export const metadata: Metadata = { + title: "Cipher Identifier — Automated Cryptanalysis | CryptoViz", + description: + "Identify unknown ciphertext using automated statistical analysis. Uses frequency analysis, index of coincidence, entropy, Kasiski examination, and pattern matching to classify encryption methods.", +}; + +export default function CipherIdentifierPage() { + return ( +
+ +
+ +
+
+ ); +} diff --git a/components/cryptanalysis/CipherIdentifier.tsx b/components/cryptanalysis/CipherIdentifier.tsx new file mode 100644 index 00000000..5c2538fb --- /dev/null +++ b/components/cryptanalysis/CipherIdentifier.tsx @@ -0,0 +1,707 @@ +"use client"; + +import { useState, useMemo, useCallback } from "react"; +import { + identifyCipher, + type IdentificationReport, + type CipherCandidate, + type AnalysisResult, + ENGLISH_FREQUENCIES, +} from "../../lib/cryptanalysis/cipherIdentifier"; +import { + Search, + Fingerprint, + BarChart3, + Activity, + Eye, + ChevronDown, + ChevronUp, + AlertTriangle, + Zap, + Target, + Info, + Clipboard, + Check, +} from "lucide-react"; +import { cn } from "../../lib/utils"; + +/* ─── Sample Ciphertexts for Educational Purposes ─────────────────────────── */ + +const SAMPLE_TEXTS = [ + { + label: "Caesar Cipher (shift 3)", + text: "KHOOR ZRUOG", + hint: "Simple shift cipher — notice how frequency patterns are preserved.", + }, + { + label: "Vigenère Cipher", + text: "LXFOPVEFRNHR", + hint: "Polyalphabetic — letter frequencies are flattened compared to English.", + }, + { + label: "Rot13", + text: "URYYB JBEYQ", + hint: "Fixed Caesar shift of 13 — self-inverse cipher.", + }, + { + label: "Hexadecimal Encoding", + text: "48656c6c6f20576f726c6421", + hint: "Only hex characters (0-9, a-f). Each pair represents one byte.", + }, + { + label: "Base64 Encoding", + text: "SGVsbG8gV29ybGQh", + hint: "Base64 alphabet (A-Z, a-z, 0-9, +, /). Padded with '='.", + }, + { + label: "Binary Encoding", + text: "01001000 01100101 01101100 01101100 01101111", + hint: "Only 0s and 1s. Each 8-bit group is one ASCII character.", + }, + { + label: "Atbash Cipher", + text: "SVOOL DLIOW", + hint: "Alphabet mirror: A→Z, B→Y, C→X, etc.", + }, + { + label: "Monoalphabetic Substitution", + text: "WKLV LV D VHFUHW PHVVDJH", + hint: "Fixed letter mapping — frequency patterns match English.", + }, + { + label: "Hex Cipher Text (AES-like)", + text: "2b7e151628aed2a6abf7158809cf4f3c", + hint: "Looks like encrypted data — high entropy, non-alphabetic.", + }, + { + label: "Rail Fence (3 rails)", + text: "WSFOAH TEE CV EDLN RCEOAT", + hint: "Transposition — letters are the same, just rearranged.", + }, +] + +/* ─── Confidence Badge ────────────────────────────────────────────────────── */ + +function ConfidenceBadge({ confidence }: { confidence: number }) { + let colorClass = "bg-zinc-500/10 text-zinc-400 border-zinc-500/20" + if (confidence >= 80) colorClass = "bg-emerald-500/10 text-emerald-400 border-emerald-500/20" + else if (confidence >= 50) colorClass = "bg-amber-500/10 text-amber-400 border-amber-500/20" + else if (confidence >= 25) colorClass = "bg-orange-500/10 text-orange-400 border-orange-500/20" + + return ( + + {confidence.toFixed(1)}% + + ) +} + +/* ─── Frequency Bar Chart ─────────────────────────────────────────────────── */ + +function FrequencyChart({ analysis }: { analysis: AnalysisResult }) { + const maxFreq = Math.max( + ...analysis.frequencies.map((f) => f.frequency), + ...Object.values(ENGLISH_FREQUENCIES) + ) + + const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + const letterData = alphabet.split("").map((letter) => { + const entry = analysis.frequencies.find((f) => f.letter === letter) + return { + letter, + observed: entry ? entry.frequency : 0, + expected: ENGLISH_FREQUENCIES[letter] || 0, + } + }) + + return ( +
+ {letterData.map((d) => ( +
+
+ {/* Expected bar */} +
+ {/* Observed bar */} +
+
+ {d.letter} +
+ ))} +
+ ) +} + +/* ─── Candidate Card ──────────────────────────────────────────────────────── */ + +function CandidateCard({ + candidate, + rank, + isExpanded, + onToggle, +}: { + candidate: CipherCandidate + rank: number + isExpanded: boolean + onToggle: () => void +}) { + const [copied, setCopied] = useState(false) + + const handleCopy = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation() + navigator.clipboard.writeText(candidate.explanation) + setCopied(true) + setTimeout(() => setCopied(false), 1500) + }, + [candidate.explanation] + ) + + return ( +
+ + {isExpanded ? ( + + ) : ( + + )} + + {isExpanded && ( +
+

+ {candidate.explanation} +

+ {candidate.recommendedActions.length > 0 && ( +
+

+ Suggested Next Steps +

+
    + {candidate.recommendedActions.map((action, i) => ( +
  • + + {action} +
  • + ))} +
+
+ )} +
+ )} +
+ ) +} + +/* ─── Metric Stat Card ────────────────────────────────────────────────────── */ + +function StatCard({ + icon: Icon, + label, + value, + sublabel, + color = "text-teal-400", +}: { + icon: React.ElementType + label: string + value: string | number + sublabel?: string + color?: string +}) { + return ( +
+
+ + + {label} + +
+

{value}

+ {sublabel && ( +

{sublabel}

+ )} +
+ ) +} + +/* ─── Main Component ──────────────────────────────────────────────────────── */ + +export default function CipherIdentifier() { + const [inputText, setInputText] = useState("KHOOR ZRUOG") + const [expandedIndex, setExpandedIndex] = useState(0) + + const report: IdentificationReport | null = useMemo(() => { + if (!inputText.trim()) return null + return identifyCipher(inputText) + }, [inputText]) + + const handleSampleClick = useCallback((text: string) => { + setInputText(text) + setExpandedIndex(0) + }, []) + + const handleClear = useCallback(() => { + setInputText("") + setExpandedIndex(null) + }, []) + + return ( +
+ {/* Header Banner */} +
+
+
+

+ Cryptanalysis Tool +

+
+
+

+ Cipher Identifier +

+

+ Paste any ciphertext and run automated statistical analysis to identify the likely encryption method. Uses frequency analysis, index of coincidence, entropy, Kasiski examination, and pattern matching. +

+
+
+

+ How It Works +

+

+ The analyzer computes 12+ statistical metrics and compares them against known signatures for each cipher type. Confidence scores reflect how well the ciphertext matches each cipher's characteristics. +

+
+
+
+
+ + {/* Sample Texts */} +
+

+ Quick Examples — Click to Analyze +

+
+ {SAMPLE_TEXTS.map((sample) => ( + + ))} +
+
+ +
+ {/* Left Column: Input & Stats */} +
+ {/* Input Section */} +
+

+ Input Ciphertext +

+

+ Paste any encrypted text, encoding, or ciphertext for analysis. +

+
+
+