/*
Description: Tool to crack private ssh keys using a dictionary.
Auth0r: sml@lacashita.com

Use it only as educational purpose.
*/

package main

import (
	"bufio"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"sync"

	"golang.org/x/crypto/ssh"
)

func prepareWordlist(jobs chan string, wordlist *string) {
	file, _ := os.Open(*wordlist)
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		jobs <- scanner.Text()
	}
	close(jobs)
}

func crackKey(jobs chan string, wg *sync.WaitGroup, b []byte, verbo *bool) ssh.AuthMethod {
	defer wg.Done()
	var key ssh.Signer
	var err error
	for {
		pazz, ok := <-jobs
		if !ok {
			break
		} else {
			key, err = ssh.ParsePrivateKeyWithPassphrase(b, []byte(pazz))
			if err != nil  { 
				if *verbo == true {
								fmt.Printf("Trying %v\n", pazz)
				}
			} else {
				fmt.Printf("[+] Valid key Found: %v\n", pazz)
				os.Exit(0)
			}
		}
	}
	return ssh.PublicKeys(key)
}

func checkKey(keyfile *string) {
	var file string
	file = *keyfile
	_, err := os.Stat(file)
	if err != nil {
		fmt.Println("Key doesnt exist")
		os.Exit(1)
	}

}

func checkWordlist(keyfile *string) {
	var file string
	file = *keyfile
	_, err := os.Stat(file)
	if err != nil {
		fmt.Println("Wordlist doesnt exist")
		os.Exit(1)
	}
}

func menu(options int) {
	if options < 5 {
		fmt.Println(`
Parameters:
-w Wordlist with passwords.
-k SSH Key to crack.
-c Number of concurrent processes.
-v Verbose

Examples of usage:
fssh -w rockyou.txt -k id_rsa 

Example with 5 concurrent processes:
fssh -w rockyou.txt -k id_rsa -c 5
		`)
		os.Exit(1)
	}
}

func main() {
	var b []byte
	var wordlist string
	var keyfile string
	var verbo bool
	var concurrent int
	var wg sync.WaitGroup
	jobs := make(chan string)
	flag.StringVar(&wordlist, "w", "", "Like /usr/share/wordlists/rockyou.txt")
	flag.StringVar(&keyfile, "k", "", "Like ~/.ssh/id_rsa")
	flag.IntVar(&concurrent, "c", 3, "Concurrency, by default 3")
	flag.BoolVar(&verbo, "v", false , "Enable Verbose")
	flag.Parse()
	menu(len(os.Args))
	checkKey(&keyfile)
	checkWordlist(&wordlist)
	fmt.Printf(`
   __ _____ _____ _    _ 
  / _/ ____/ ____| |  | |
 | || (___| (___ | |__| |
 |  _\___ \\___ \|  __  |
 | | ____) |___) | |  | |
 |_||_____/_____/|_|  |_|
===========================

[-] Cracking the key.... Wait.

`)

	b, _ = ioutil.ReadFile(keyfile)
	go prepareWordlist(jobs, &wordlist)
	for i := 0; i < concurrent; i++ {
		go crackKey(jobs, &wg, b, &verbo)
		wg.Add(1)
	}
	wg.Wait()
	fmt.Printf("[x] Password not found :_(\n")
}
