package main import ( "bytes" "fmt" "io/ioutil" "log" "math/rand" "sort" "strings" "time" ) var adfgvx = "ADFGVX" var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" func distinct(bs []byte) []byte { var u []byte for _, b := range bs { if !bytes.Contains(u, []byte{b}) { u = append(u, b) } } return u } func allAsciiAlphaNum(word []byte) bool { for _, b := range word { if !((b >= 48 && b <= 57) || (b >= 65 && b <= 90) || (b >= 97 && b <= 122)) { return false } } return true } func orderKey(key string) []int { temp := make([][2]byte, len(key)) for i := 0; i < len(key); i++ { temp[i] = [2]byte{key[i], byte(i)} } sort.Slice(temp, func(i, j int) bool { return temp[i][0] < temp[j][0] }) res := make([]int, len(key)) for i := 0; i < len(key); i++ { res[i] = int(temp[i][1]) } return res } func createPolybius() []string { temp := []byte(alphabet) rand.Shuffle(36, func(i, j int) { temp[i], temp[j] = temp[j], temp[i] }) alphabet = string(temp) fmt.Println("6 x 6 Polybius square:\n") fmt.Println(" | A D F G V X") fmt.Println("---------------") p := make([]string, 6) for i := 0; i < 6; i++ { fmt.Printf("%c | ", adfgvx[i]) p[i] = alphabet[6*i : 6*(i+1)] for _, c := range p[i] { fmt.Printf("%c ", c) } fmt.Println() } return p } func createKey(n int) string { if n < 7 || n > 12 { log.Fatal("Key should be within 7 and 12 letters long.") } bs, err := ioutil.ReadFile("unixdict.txt") if err != nil { log.Fatal("Error reading file") } words := bytes.Split(bs, []byte{'\n'}) var candidates [][]byte for _, word := range words { if len(word) == n && len(distinct(word)) == n && allAsciiAlphaNum(word) { candidates = append(candidates, word) } } k := string(bytes.ToUpper(candidates[rand.Intn(len(candidates))])) fmt.Println("\nThe key is", k) return k } func encrypt(polybius []string, key, plainText string) string { temp := "" outer: for _, ch := range []byte(plainText) { for r := 0; r <= 5; r++ { for c := 0; c <= 5; c++ { if polybius[r][c] == ch { temp += fmt.Sprintf("%c%c", adfgvx[r], adfgvx[c]) continue outer } } } } colLen := len(temp) / len(key) // all columns need to be the same length if len(temp)%len(key) > 0 { colLen++ } table := make([][]string, colLen) for i := 0; i < colLen; i++ { table[i] = make([]string, len(key)) } for i := 0; i < len(temp); i++ { table[i/len(key)][i%len(key)] = string(temp[i]) } order := orderKey(key) cols := make([][]string, len(key)) for i := 0; i < len(key); i++ { cols[i] = make([]string, colLen) for j := 0; j < colLen; j++ { cols[i][j] = table[j][order[i]] } } res := make([]string, len(cols)) for i := 0; i < len(cols); i++ { res[i] = strings.Join(cols[i], "") } return strings.Join(res, " ") } func decrypt(polybius []string, key, cipherText string) string { colStrs := strings.Split(cipherText, " ") // ensure all columns are same length maxColLen := 0 for _, s := range colStrs { if len(s) > maxColLen { maxColLen = len(s) } } cols := make([][]string, len(colStrs)) for i, s := range colStrs { var ls []string for _, c := range s { ls = append(ls, string(c)) } if len(s) < maxColLen { cols[i] = make([]string, maxColLen) copy(cols[i], ls) } else { cols[i] = ls } } table := make([][]string, maxColLen) order := orderKey(key) for i := 0; i < maxColLen; i++ { table[i] = make([]string, len(key)) for j := 0; j < len(key); j++ { table[i][order[j]] = cols[j][i] } } temp := "" for i := 0; i < len(table); i++ { temp += strings.Join(table[i], "") } plainText := "" for i := 0; i < len(temp); i += 2 { r := strings.IndexByte(adfgvx, temp[i]) c := strings.IndexByte(adfgvx, temp[i+1]) plainText = plainText + string(polybius[r][c]) } return plainText } func main() { rand.Seed(time.Now().UnixNano()) plainText := "ATTACKAT1200AM" polybius := createPolybius() key := createKey(9) fmt.Println("\nPlaintext :", plainText) cipherText := encrypt(polybius, key, plainText) fmt.Println("\nEncrypted :", cipherText) plainText2 := decrypt(polybius, key, cipherText) fmt.Println("\nDecrypted :", plainText2) }