For a puzzle I had to brute force a password. I've never used goroutines before, and I don't have much experience in concurrency.
This is the cksum function, which takes an array of the ASCII code for each letter in a string (a is 97, b is 98...etc):
func cksum(bytes []int) int{
a := 0
b := 0
for i := 0; i < len(bytes); i++ {
a = (a + bytes[i]) % 255
b = (b + a) % 255
}
return (b << 8) | a;
}
The password was a-zA-z
of even length, and greater than a length of 10. The left and right halves had to output a certain value when run through cksum, and when put together, the even letters of the password had to output a certain value when run through cksum.
The left half would have to output: 53358
The right half would have to output: 61453
And together, the even letters would output: 0
I came up with the following code that assumed the password was 12 characters (it could be more characters).
Launches a goroutine for each of the 52 possible letters.
Each goroutine generates every single possible combination for a 6 letter string where the first letter is the one the goroutine was started with.
If that six letter string matches the left hand side it is pushed into an array.
If it matches the right hand side, a new goroutine is launched that concatenates that right hand match with every single match in the left hand side array and checks if the even checksum is 0.
package main
import "fmt"
import "bytes"
var leftAnswers = []string{}
func everyEven(bytes []int) []int {
arr := []int{}
for i := 0; i < len(bytes); i++ {
if i % 2 == 0 {
arr = append(arr, bytes[i])
}
}
return arr
}
func toBytes(str string) []int{
arr := []int{}
for _,code := range str {
arr = append(arr, int(code))
}
return arr
}
func cksum(bytes []int) int{
a := 0
b := 0
for i := 0; i < len(bytes); i++ {
a = (a + bytes[i]) % 255
b = (b + a) % 255
}
return (b << 8) | a;
}
func brute(start int) {
counter := 0
alphabet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
fmt.Println("Started", start)
for a := start; a < len(alphabet); a++ {
letter1 := string(alphabet[a]);
for b := 0; b < len(alphabet); b++ {
letter2 := string(alphabet[b]);
for c := 0; c < len(alphabet); c++ {
letter3 := string(alphabet[c]);
for d := 0; d < len(alphabet); d++ {
letter4 := string(alphabet[d]);
for e := 0; e < len(alphabet); e++ {
letter5 := string(alphabet[e]);
for f := 0; f < len(alphabet); f++ {
letter6 := string(alphabet[f]);
counter += 1
var buffer bytes.Buffer
buffer.WriteString(letter1)
buffer.WriteString(letter2)
buffer.WriteString(letter3)
buffer.WriteString(letter4)
buffer.WriteString(letter5)
buffer.WriteString(letter6)
pw := buffer.String()
checksum := cksum(toBytes(pw))
go lhs(pw, checksum)
go rhs(pw, checksum)
if (counter % 1000000 == 0) {
fmt.Println(start, counter, " processed")
}
}
}
}
}
}
}
}
func lhs(pw string, checksum int) {
if checksum == 53358 {
leftAnswers = append(leftAnswers, pw)
}
}
func rhs(pw string, checksum int) {
if checksum == 61453 {
go findPassword(pw)
}
}
func findPassword(pw string) {
for i := 0; i < len(leftAnswers); i++ {
fullpw := leftAnswers[i] + pw
bytes := toBytes(fullpw)
evens := everyEven(bytes)
if cksum(evens) == 0 {
fmt.Println("THE PASSWORD IS:", fullpw)
}
}
}
func main() {
for i := 0; i < 52; i++ {
go brute(i)
}
select {}
}
It eventually did the job in ~15 minutes, but I don't know if I used concurrency correctly at all. I just spawned a goroutine as frequently as possible to be honest because I assumed more concurrency = faster code. Could someone help me out?