I'm adapting code from a C program I made in class & I'm trying to convert all the programs I've written in C into Go in order to learn the language. I'm not quite "getting" concurrency yet though. How would I apply concurrency to a nested for loop? The current iteration of my program is SLOW, much slower than if i would have written in C.
Here is my code:
package main
import (
"fmt"
"os"
"unsafe"
)
// #cgo LDFLAGS: -lcrypt
// #define _GNU_SOURCE
// #include <crypt.h>
// #include <stdlib.h>
import "C"
// credit for this solution goes to https://stackoverflow.com/questions/14109915/what-is-gos-equivalent-to-pythons-crypt-crypt
// for showing that you can wrap go in C via CGO
// crypt wraps C library crypt_r
func crypt(key, salt string) string {
data := C.struct_crypt_data{}
ckey := C.CString(key)
csalt := C.CString(salt)
out := C.GoString(C.crypt_r(ckey, csalt, &data))
C.free(unsafe.Pointer(ckey))
C.free(unsafe.Pointer(csalt))
return out
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: ./cracker k")
return
}
cipher := os.Args[1]
var guess [5]byte
for i := 65; i < 123; i++ {
if i >= 91 && i <= 96 {
} else {
guess[0] = byte(i)
if cipher == crypt(string(guess[:1]), "50") {
fmt.Println(string(guess[:1]))
return
}
fmt.Println(string(guess[:1]))
for j := 65; j < 123; j++ {
if j >= 91 && j <= 96 {
} else {
guess[1] = byte(j)
if cipher == crypt(string(guess[:2]), "50") {
fmt.Println(string(guess[:2]))
return
}
fmt.Println(string(guess[:2]))
for k := 65; k < 123; k++ {
if k >= 91 && k <= 96 {
} else {
guess[2] = byte(k)
if cipher == crypt(string(guess[:3]), "50") {
fmt.Println(string(guess[:3]))
return
}
fmt.Println(string(guess[:3]))
for l := 65; l < 123; l++ {
if l >= 91 && l <= 96 {
} else {
guess[3] = byte(l)
if cipher == crypt(string(guess[:4]), "50") {
fmt.Println(string(guess[:4]))
return
}
fmt.Println(string(guess[:4]))
for m := 65; m < 123; m++ {
if m >= 91 && m <= 96 {
} else {
guess[4] = byte(m)
if cipher == crypt(string(guess[:5]), "50") {
fmt.Println(string(guess[:5]))
return
}
fmt.Println(string(guess[:5]))
}
}
}
}
}
}
}
}
}
}
}
The purpose of the program is to brute force a DES hash, it will work on any password up to 5 characters in length (thus, 5 nested for loops).
I will not rewrite your code but I can give you a sample solution so you can fill in blanks.
package main
import (
"fmt"
"runtime"
"sync"
)
func produce(in chan string) {
defer close(in)
for i := 0; i < 1000; i++ {
in <- fmt.Sprintf("%d", i)
}
}
func consume(in, out chan string, wg *sync.WaitGroup) {
defer wg.Done()
for s := range in {
if s == "500" {
out <- s
}
}
}
func stop(out chan string, wg *sync.WaitGroup) {
wg.Wait()
close(out)
}
func main() {
in, out := make(chan string), make(chan string)
wg := &sync.WaitGroup{}
go produce(in)
for i := 0; i < runtime.NumCPU(); i++ {
wg.Add(1)
go consume(in, out, wg)
}
go stop(out, wg)
fmt.Println(<-out)
}
The idea is to produce password proposals in producer and push heavy computation into consumers. There will be as many consumers as CPUs. Producer signals end of work by closing in
channel. Consumers pick up the next password proposal form the in
channel and try computing the hash. If they succeed they put the password into the out
channel.
Stopping the program is a little bit tricky. We need a stoping routine to wait for either all consumers or for the cracked password.