package main import ( "bufio" "crypto/rand" "crypto/sha256" "encoding/binary" "flag" "fmt" "io" "math" "os" "strings" "syscall" "time" "filippo.io/age" "github.com/awnumar/memguard" "github.com/gdamore/tcell/v2" "golang.org/x/term" ) const ( version = "3.1.0" minLength = 8 maxLength = 256 defaultLength = 16 charSet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}|;:,.<>?/" defaultOutputFile = "password.txt.age" challenge1Target = 512 // bytes di entropia - challenge 1 (tastiera) challenge2Target = 512 // bytes di entropia - challenge 2 (mouse) entropyTimeout = 90 * time.Second wipePassCount = 7 // DoD 5220.22-M standard ) type EntropySource struct { data []byte hash []byte startTime time.Time timings []int64 // nanosecondi tra eventi eventType string // "keyboard" o "mouse" } func NewEntropySource(eventType string) *EntropySource { return &EntropySource{ data: make([]byte, 0, 512), timings: make([]int64, 0, 256), startTime: time.Now(), eventType: eventType, } } func (es *EntropySource) AddByte(b byte, timestamp int64) { es.data = append(es.data, b) // Aggiungi timing se non è il primo byte if len(es.timings) > 0 { delta := timestamp - es.timings[len(es.timings)-1] // Converti delta in bytes (8 bytes) buf := make([]byte, 8) binary.LittleEndian.PutUint64(buf, uint64(delta)) es.data = append(es.data, buf...) } es.timings = append(es.timings, timestamp) } // AddMouseEvent aggiunge entropia da coordinate e bottoni mouse func (es *EntropySource) AddMouseEvent(x, y int, button uint8, timestamp int64) { // Aggiungi coordinate come entropia buf := make([]byte, 8) binary.LittleEndian.PutUint32(buf[0:4], uint32(x)) binary.LittleEndian.PutUint32(buf[4:8], uint32(y)) es.data = append(es.data, buf...) // Aggiungi stato bottone es.data = append(es.data, button) // Aggiungi timing delta if len(es.timings) > 0 { delta := timestamp - es.timings[len(es.timings)-1] timeBuf := make([]byte, 8) binary.LittleEndian.PutUint64(timeBuf, uint64(delta)) es.data = append(es.data, timeBuf...) } es.timings = append(es.timings, timestamp) } func (es *EntropySource) Finalize() { // Hash finale di tutti i dati + timings h := sha256.Sum256(es.data) es.hash = h[:] } func (es *EntropySource) Size() int { return len(es.data) } func (es *EntropySource) TimingStats() (avgDelta, stdDev float64) { if len(es.timings) < 2 { return 0, 0 } // Calcola media dei delta var sum int64 deltas := make([]int64, len(es.timings)-1) for i := 1; i < len(es.timings); i++ { delta := es.timings[i] - es.timings[i-1] deltas[i-1] = delta sum += delta } avgDelta = float64(sum) / float64(len(deltas)) // Calcola deviazione standard var variance float64 for _, delta := range deltas { diff := float64(delta) - avgDelta variance += diff * diff } variance /= float64(len(deltas)) stdDev = float64(int64(variance)) // sqrt approssimato return avgDelta / 1000000, stdDev / 1000000 // converti in millisecondi } // Challenge 1: Digitazione VELOCE e CAOTICA con TASTIERA func collectKeyboardEntropy(target int, verbose bool) (*EntropySource, error) { es := NewEntropySource("keyboard") fmt.Println("\n⌨️ CHALLENGE 1: KEYBOARD CHAOS") fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") fmt.Printf("📊 Target: %d bytes\n", target) fmt.Println("⏱️ Timeout: 90 seconds") fmt.Println("💡 Type FAST and RANDOMLY:") fmt.Println(" • Slam the keyboard chaotically") fmt.Println(" • Mix letters, numbers, symbols") fmt.Println(" • Don't think, just type!") fmt.Println(" • The more chaotic, the better!") fmt.Println("✅ Press ENTER when done\n") // Imposta terminal in raw mode oldState, err := term.MakeRaw(int(syscall.Stdin)) if err != nil { return nil, fmt.Errorf("failed to set raw mode: %w", err) } defer term.Restore(int(syscall.Stdin), oldState) reader := bufio.NewReader(os.Stdin) done := make(chan bool) timeout := time.After(entropyTimeout) go func() { lastUpdate := time.Now() for { char, err := reader.ReadByte() if err != nil { break } currentTime := time.Now().UnixNano() // ENTER per finire if char == 0x0D || char == 0x0A { if es.Size() >= target { done <- true return } continue } // Ignora caratteri di controllo (tranne ESC, TAB) if char < 32 && char != 27 && char != 9 { continue } // Aggiungi byte con timestamp es.AddByte(char, currentTime) // Mostra progresso if time.Since(lastUpdate) > 100*time.Millisecond { progress := float64(es.Size()) * 100.0 / float64(target) if progress > 100 { progress = 100 } // Calcola WPM (words per minute) approssimato elapsed := time.Since(es.startTime).Seconds() wpm := int((float64(len(es.timings)) / 5) / elapsed * 60) fmt.Printf("\r🔄 Progress: %.1f%% (%d/%d bytes) | Speed: ~%d WPM", progress, es.Size(), target, wpm) lastUpdate = time.Now() if es.Size() >= target { fmt.Println() done <- true return } } } }() select { case <-done: fmt.Println("✅ Keyboard challenge completed!") case <-timeout: if es.Size() < target/2 { return nil, fmt.Errorf("insufficient entropy collected (timeout)") } fmt.Println("\n⏱️ Timeout reached, using collected data") } // Finalizza con hash es.Finalize() if verbose { avgDelta, stdDev := es.TimingStats() fmt.Printf("📊 Collected: %d bytes\n", es.Size()) fmt.Printf("⌨️ Keystrokes: %d\n", len(es.timings)) fmt.Printf("⏱️ Avg timing: %.2f ms (±%.2f ms)\n", avgDelta, stdDev) fmt.Printf("🔐 Hash: %x...\n", es.hash[:8]) } return es, nil } // Challenge 2: Movimento MOUSE caotico func collectMouseEntropy(target int, verbose bool) (*EntropySource, error) { es := NewEntropySource("mouse") // Mostra istruzioni PRIMA di inizializzare tcell fmt.Println("\n🖱️ CHALLENGE 2: MOUSE CHAOS") fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") fmt.Printf("📊 Target: %d bytes\n", target) fmt.Println("⏱️ Timeout: 90 seconds") fmt.Println("💡 Move mouse CHAOTICALLY:") fmt.Println(" • Move in random patterns") fmt.Println(" • Click randomly (left, right, middle)") fmt.Println(" • Use scroll wheel") fmt.Println(" • Fast movements + sudden stops") fmt.Println(" • Circles, zigzags, random paths") fmt.Println("✅ Press 'Q' or ESC when done\n") fmt.Println("🚀 Starting mouse capture in 2 seconds...") time.Sleep(2 * time.Second) // Inizializza tcell screen screen, err := tcell.NewScreen() if err != nil { return nil, fmt.Errorf("failed to create screen: %w", err) } if err := screen.Init(); err != nil { return nil, fmt.Errorf("failed to init screen: %w", err) } defer screen.Fini() // Abilita mouse tracking screen.EnableMouse(tcell.MouseMotionEvents) screen.Clear() // Variabili per tracking var lastX, lastY int var totalDistance float64 var clicks, scrollEvents int startTime := time.Now() // Funzione per disegnare UI drawUI := func() { screen.Clear() w, h := screen.Size() // Titolo drawString(screen, 0, 0, "🖱️ MOUSE ENTROPY COLLECTION", tcell.StyleDefault.Bold(true)) drawString(screen, 0, 1, strings.Repeat("━", w), tcell.StyleDefault) // Progress progress := float64(es.Size()) * 100.0 / float64(target) if progress > 100 { progress = 100 } progressBar := makeProgressBar(progress, w-20) drawString(screen, 0, 3, fmt.Sprintf("Progress: %s %.1f%%", progressBar, progress), tcell.StyleDefault) // Stats drawString(screen, 0, 5, fmt.Sprintf("📊 Bytes: %d/%d", es.Size(), target), tcell.StyleDefault) drawString(screen, 0, 6, fmt.Sprintf("🖱️ Events: %d", len(es.timings)), tcell.StyleDefault) drawString(screen, 0, 7, fmt.Sprintf("👆 Clicks: %d", clicks), tcell.StyleDefault) drawString(screen, 0, 8, fmt.Sprintf("📜 Scrolls: %d", scrollEvents), tcell.StyleDefault) drawString(screen, 0, 9, fmt.Sprintf("📏 Distance: %.0f px", totalDistance), tcell.StyleDefault) // Tempo rimanente elapsed := time.Since(startTime) remaining := entropyTimeout - elapsed if remaining < 0 { remaining = 0 } drawString(screen, 0, 11, fmt.Sprintf("⏱️ Time: %.0fs remaining", remaining.Seconds()), tcell.StyleDefault) // Istruzioni drawString(screen, 0, h-3, "💡 Move mouse chaotically, click, scroll!", tcell.StyleDefault) drawString(screen, 0, h-2, "✅ Press Q or ESC when progress reaches 100%", tcell.StyleDefault) // Visualizzazione movimento (area centrale) if lastX > 0 && lastY > 0 && lastX < w && lastY < h-4 && lastY > 12 { screen.SetContent(lastX, lastY, '◉', nil, tcell.StyleDefault.Foreground(tcell.ColorGreen)) } screen.Show() } // Loop principale done := make(chan bool) timeout := time.After(entropyTimeout) go func() { for { ev := screen.PollEvent() currentTime := time.Now().UnixNano() switch ev := ev.(type) { case *tcell.EventMouse: x, y := ev.Position() buttons := ev.Buttons() // Calcola distanza dal punto precedente if lastX > 0 || lastY > 0 { dx := float64(x - lastX) dy := float64(y - lastY) dist := math.Sqrt(dx*dx + dy*dy) totalDistance += dist } // Determina tipo evento var buttonByte uint8 switch { case buttons&tcell.Button1 != 0: buttonByte = 1 clicks++ case buttons&tcell.Button2 != 0: buttonByte = 2 clicks++ case buttons&tcell.Button3 != 0: buttonByte = 3 clicks++ case buttons&tcell.WheelUp != 0: buttonByte = 4 scrollEvents++ case buttons&tcell.WheelDown != 0: buttonByte = 5 scrollEvents++ default: buttonByte = 0 // movimento senza click } // Aggiungi entropia solo se c'è movimento o azione if x != lastX || y != lastY || buttonByte > 0 { es.AddMouseEvent(x, y, buttonByte, currentTime) } lastX, lastY = x, y drawUI() if es.Size() >= target { done <- true return } case *tcell.EventKey: if ev.Key() == tcell.KeyEscape || ev.Rune() == 'q' || ev.Rune() == 'Q' { if es.Size() >= target/2 { done <- true return } } case *tcell.EventResize: screen.Sync() drawUI() } } }() // Disegna UI iniziale drawUI() select { case <-done: // Completato case <-timeout: if es.Size() < target/2 { return nil, fmt.Errorf("insufficient entropy collected (timeout)") } } // Finalizza screen.Fini() es.Finalize() fmt.Println("✅ Mouse challenge completed!") if verbose { avgDelta, stdDev := es.TimingStats() fmt.Printf("📊 Collected: %d bytes\n", es.Size()) fmt.Printf("🖱️ Events: %d\n", len(es.timings)) fmt.Printf("👆 Clicks: %d\n", clicks) fmt.Printf("📜 Scrolls: %d\n", scrollEvents) fmt.Printf("📏 Total distance: %.0f pixels\n", totalDistance) fmt.Printf("⏱️ Avg timing: %.2f ms (±%.2f ms)\n", avgDelta, stdDev) fmt.Printf("🔐 Hash: %x...\n", es.hash[:8]) } return es, nil } // Helper: disegna stringa su schermo func drawString(s tcell.Screen, x, y int, str string, style tcell.Style) { for i, r := range str { s.SetContent(x+i, y, r, nil, style) } } // Helper: crea barra di progresso func makeProgressBar(percent float64, width int) string { filled := int(percent * float64(width) / 100) if filled > width { filled = width } empty := width - filled return "[" + strings.Repeat("█", filled) + strings.Repeat("░", empty) + "]" } // Combina le sorgenti di entropia func combineEntropy(keyboard, mouse *EntropySource, verbose bool) ([]byte, error) { if verbose { fmt.Println("\n🔀 COMBINING ENTROPY SOURCES") fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") } // Crea buffer combinato combined := make([]byte, 0, len(keyboard.hash)+len(mouse.hash)+32) combined = append(combined, keyboard.hash...) combined = append(combined, mouse.hash...) // Aggiungi crypto/rand cryptoBytes := make([]byte, 32) if _, err := rand.Read(cryptoBytes); err != nil { return nil, fmt.Errorf("crypto/rand failed: %w", err) } combined = append(combined, cryptoBytes...) // Hash finale finalHash := sha256.Sum256(combined) if verbose { fmt.Printf("⌨️ Keyboard hash: %x...\n", keyboard.hash[:8]) fmt.Printf("🖱️ Mouse hash: %x...\n", mouse.hash[:8]) fmt.Printf("🎲 Crypto/rand: %x...\n", cryptoBytes[:8]) fmt.Printf("🔐 Final seed: %x...\n", finalHash[:8]) // Statistiche comparative avgK, stdK := keyboard.TimingStats() avgM, stdM := mouse.TimingStats() fmt.Printf("\n📈 Timing Analysis:\n") fmt.Printf(" Keyboard: %.2f ms avg, %.2f ms variance\n", avgK, stdK) fmt.Printf(" Mouse: %.2f ms avg, %.2f ms variance\n", avgM, stdM) fmt.Printf(" Difference: %.2f ms (higher = better)\n", math.Abs(avgK-avgM)) } return finalHash[:], nil } // Genera password usando l'entropia combinata func generatePassword(length int, seed []byte, verbose bool) (*memguard.LockedBuffer, error) { if length < minLength || length > maxLength { return nil, fmt.Errorf("invalid length: must be between %d and %d", minLength, maxLength) } if verbose { fmt.Println("\n🔑 GENERATING PASSWORD") fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") fmt.Printf("📏 Length: %d characters\n", length) fmt.Printf("🎲 Charset: %d possible characters\n", len(charSet)) fmt.Printf("💪 Entropy: ~%.2f bits\n", float64(length)*6.5) } // Crea buffer temporaneo per generare la password tempPass := make([]byte, length) // Usa seed come fonte per PRNG deterministic reader := sha256.New() reader.Write(seed) for i := 0; i < length; i++ { // Genera nuovo hash ad ogni iterazione per più entropia reader.Write(seed) reader.Write([]byte{byte(i)}) hash := reader.Sum(nil) // Usa hash per scegliere carattere idx := binary.BigEndian.Uint32(hash[:4]) % uint32(len(charSet)) tempPass[i] = charSet[idx] // Re-seed per prossima iterazione reader.Reset() reader.Write(hash) } // Crea buffer protetto con memguard dalla password generata password := memguard.NewBufferFromBytes(tempPass) defer func() { if r := recover(); r != nil { password.Destroy() panic(r) } }() // Pulisci buffer temporaneo memguard.WipeBytes(tempPass) if verbose { fmt.Println("✅ Password generated in protected memory") } return password, nil } // Cripta password con Age func encrypt(password *memguard.LockedBuffer, passphrase, filename string) error { recipient, err := age.NewScryptRecipient(passphrase) if err != nil { return fmt.Errorf("failed to create recipient: %w", err) } out, err := os.Create(filename) if err != nil { return fmt.Errorf("failed to create output file: %w", err) } defer out.Close() w, err := age.Encrypt(out, recipient) if err != nil { return fmt.Errorf("failed to create encrypted writer: %w", err) } if _, err := w.Write(password.Bytes()); err != nil { return fmt.Errorf("failed to write encrypted data: %w", err) } if err := w.Close(); err != nil { return fmt.Errorf("failed to close encrypted writer: %w", err) } return nil } // Decripta password func decrypt(passphrase, filename string, verbose bool) error { if verbose { fmt.Println("\n🔓 DECRYPTING PASSWORD") fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") } f, err := os.Open(filename) if err != nil { return fmt.Errorf("failed to open file: %w", err) } defer f.Close() identity, err := age.NewScryptIdentity(passphrase) if err != nil { return fmt.Errorf("failed to create identity: %w", err) } r, err := age.Decrypt(f, identity) if err != nil { return fmt.Errorf("failed to decrypt (wrong passphrase?): %w", err) } password, err := io.ReadAll(r) if err != nil { return fmt.Errorf("failed to read decrypted data: %w", err) } defer memguard.WipeBytes(password) fmt.Printf("\n🔐 Decrypted Password: %s\n", string(password)) if verbose { fmt.Printf("📏 Length: %d characters\n", len(password)) } return nil } // Richiedi passphrase (nascosta) func promptPassphrase(prompt string) (string, error) { fmt.Print(prompt) passBytes, err := term.ReadPassword(int(syscall.Stdin)) fmt.Println() if err != nil { return "", err } return string(passBytes), nil } // Cancellazione sicura DoD 5220.22-M (7-pass) func secureWipe(filename string, verbose bool) error { if verbose { fmt.Println("\n🔥 SECURE WIPE (DoD 5220.22-M)") fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") fmt.Printf("📁 Target: %s\n", filename) fmt.Printf("🔄 Passes: %d\n\n", wipePassCount) } // Verifica file esiste stat, err := os.Stat(filename) if err != nil { return fmt.Errorf("file not found: %w", err) } fileSize := stat.Size() if verbose { fmt.Printf("📊 File size: %d bytes\n\n", fileSize) } // Pattern di sovrascrittura DoD 5220.22-M patterns := []byte{ 0x00, // Pass 1: tutti zeri 0xFF, // Pass 2: tutti uno 0x00, // Pass 3: tutti zeri 0xFF, // Pass 4: tutti uno 0x00, // Pass 5: tutti zeri 0xFF, // Pass 6: tutti uno // Pass 7: random (generato dopo) } // Esegui 7 pass for i := 0; i < wipePassCount; i++ { if verbose { fmt.Printf("🔄 Pass %d/%d: ", i+1, wipePassCount) } // Apri file in write mode f, err := os.OpenFile(filename, os.O_WRONLY, 0) if err != nil { return fmt.Errorf("failed to open file for wiping: %w", err) } var pattern []byte if i < len(patterns) { // Pattern fisso pattern = make([]byte, fileSize) for j := range pattern { pattern[j] = patterns[i] } if verbose { fmt.Printf("Writing 0x%02X pattern\n", patterns[i]) } } else { // Random pattern (pass 7) pattern = make([]byte, fileSize) rand.Read(pattern) if verbose { fmt.Println("Writing random data") } } // Scrivi pattern if _, err := f.Write(pattern); err != nil { f.Close() return fmt.Errorf("failed to write pattern: %w", err) } // Sync su disco if err := f.Sync(); err != nil { f.Close() return fmt.Errorf("failed to sync: %w", err) } f.Close() } // Rimuovi file if err := os.Remove(filename); err != nil { return fmt.Errorf("failed to remove file: %w", err) } if verbose { fmt.Println("\n✅ File securely wiped and removed!") } else { fmt.Printf("✅ %s securely wiped\n", filename) } return nil } func main() { memguard.CatchInterrupt() defer memguard.Purge() // Flags length := flag.Int("l", defaultLength, "Password length") output := flag.String("o", defaultOutputFile, "Output encrypted file") decryptMode := flag.Bool("decrypt", false, "Decrypt mode") encfile := flag.String("encfile", defaultOutputFile, "Encrypted file to decrypt") wipeMode := flag.Bool("wipe", false, "Secure wipe mode") wipefile := flag.String("wipefile", "", "File to securely wipe") verbose := flag.Bool("v", false, "Verbose output") showVersion := flag.Bool("version", false, "Show version") flag.Parse() // Version if *showVersion { fmt.Printf("gensecpass3 v%s\n", version) return } // Wipe mode if *wipeMode { if *wipefile == "" { fmt.Println("❌ Error: -wipefile required for wipe mode") os.Exit(1) } fmt.Printf("⚠️ WARNING: This will PERMANENTLY destroy: %s\n", *wipefile) fmt.Print("Are you absolutely sure? (type 'YES' to confirm): ") reader := bufio.NewReader(os.Stdin) response, _ := reader.ReadString('\n') if strings.TrimSpace(response) != "YES" { fmt.Println("❌ Wipe cancelled") os.Exit(0) } if err := secureWipe(*wipefile, *verbose); err != nil { fmt.Printf("❌ Wipe failed: %v\n", err) os.Exit(1) } return } // Decrypt mode if *decryptMode { pass, err := promptPassphrase("Enter passphrase: ") if err != nil { fmt.Printf("❌ Error: %v\n", err) os.Exit(1) } if err := decrypt(pass, *encfile, *verbose); err != nil { fmt.Printf("❌ Decryption failed: %v\n", err) os.Exit(1) } return } // Generate mode fmt.Printf("╔════════════════════════════════════════════════════╗\n") fmt.Printf("║ 🔐 GENSECPASS3 v%s ║\n", version) fmt.Printf("║ Ultra-Secure Password Generator ║\n") fmt.Printf("║ Keyboard + Mouse Entropy Collection ║\n") fmt.Printf("╚════════════════════════════════════════════════════╝\n") // Step 1: Challenge 1 - Keyboard keyboard, err := collectKeyboardEntropy(challenge1Target, *verbose) if err != nil { fmt.Printf("❌ Keyboard challenge failed: %v\n", err) os.Exit(1) } // Step 2: Challenge 2 - Mouse mouse, err := collectMouseEntropy(challenge2Target, *verbose) if err != nil { fmt.Printf("❌ Mouse challenge failed: %v\n", err) os.Exit(1) } // Step 3: Combine entropy seed, err := combineEntropy(keyboard, mouse, *verbose) if err != nil { fmt.Printf("❌ Entropy combination failed: %v\n", err) os.Exit(1) } // Step 4: Generate password password, err := generatePassword(*length, seed, *verbose) if err != nil { fmt.Printf("❌ Password generation failed: %v\n", err) os.Exit(1) } defer password.Destroy() // Step 5: Save or display fmt.Println("\n💾 SAVE OPTIONS") fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") fmt.Print("Save encrypted password to file? (y/N): ") reader := bufio.NewReader(os.Stdin) response, _ := reader.ReadString('\n') if strings.TrimSpace(strings.ToLower(response)) == "y" { pass, err := promptPassphrase("Enter passphrase: ") if err != nil { fmt.Printf("❌ Error: %v\n", err) os.Exit(1) } confirm, err := promptPassphrase("Confirm passphrase: ") if err != nil { fmt.Printf("❌ Error: %v\n", err) os.Exit(1) } if pass != confirm { fmt.Println("❌ Passphrases don't match!") os.Exit(1) } if err := encrypt(password, pass, *output); err != nil { fmt.Printf("❌ Encryption failed: %v\n", err) os.Exit(1) } fmt.Printf("\n✅ Password saved to: %s\n", *output) } else { fmt.Println("\n🔐 Generated Password:", password.String()) fmt.Println("⚠️ Password displayed only (not saved)") } fmt.Println("\n🧹 Password destroyed from memory") fmt.Println("✅ Session complete!") }