func BreakXorRepeating(ciphertext []byte, keysizes []int) ([]byte, error) { if len(ciphertext) == 0 { return nil, errors.New("empty ciphertext") } if len(keysizes) == 0 { return nil, errors.New("no keysizes given") } var bestScore = 0.0 var plaintext []byte for _, size := range keysizes { split, _ := bytes.SplitIntoBlocks(ciphertext, size) transposed, err := blocks.Transpose(split) if err != nil { return nil, err } key := make([]byte, size) for i, block := range transposed { popular, _ := bytes.Popular(block, 1) key[i] = popular[0] } decrypted, _ := bytes.XorRepeatingKey(ciphertext, key) score := utils.EnglishScore(string(decrypted)) if score > bestScore { plaintext = decrypted bestScore = score } } return plaintext, nil }
func TestFindKeysizes(t *testing.T) { input := []byte("And you're going to do it all anew. Better run for the hills.") tooShort := []byte("Too short") empty := []byte("") key := []byte("YELLOW") size := len(key) n := 3 // Invalid inputs encrypted, _ := bytes.XorRepeatingKey(tooShort, key) _, err := FindKeysizes(encrypted, n, 3, 10) if err.Error() != "input not long enough for analysis" { t.Error("should fail if ciphertext is not 4x as long as maxSize") } _, err = FindKeysizes(empty, n, 3, 10) if err.Error() != "empty input ciphertext" { t.Error("should fail given a nil ciphertext") } _, err = FindKeysizes(encrypted, n, 10, 3) if err.Error() != "minSize > maxSize" { t.Error("should fail if minSize > maxSize") } _, err = FindKeysizes(encrypted, 0, 3, 10) if err.Error() != "n must be > 0" { t.Error("should fail if n == 0") } // Valid input encrypted, err = bytes.XorRepeatingKey(input, key) keysizes, err := FindKeysizes(encrypted, n, 3, 10) if err != nil { t.Error(err) } if len(keysizes) != n { t.Error("expected %d keysizes, got %d", n, len(keysizes)) } guessed := false for i := range keysizes { if keysizes[i] == size { guessed = true } } if !guessed { t.Error("guessed wrong key length") } }
// Implement repeating-key XOR func c5() (actual, expected Result) { input := []byte("Burning 'em, if you ain't quick and nimble\nI go crazy when I hear a cymbal") expected = "0b3637272a2b2e63622c2e69692a23693a2a3c6324202d623d63343c2a26226324272765272a282b2f20430a652e2c652a3124333a653e2b2027630c692b20283165286326302e27282f" key := []byte("ICE") encrypted, err := bytes.XorRepeatingKey(input, key) if err != nil { log.Fatal(err) } encryptedStr := hex.EncodeToString(encrypted) return encryptedStr, expected }
// Detect single-character XOR func c4() (actual, expected Result) { expected = string([]byte{110, 79, 87, 0, 84, 72, 65, 84, 0, 84, 72, 69, 0, 80, 65, 82, 84, 89, 0, 73, 83, 0, 74, 85, 77, 80, 73, 78, 71, 42}) input, err := ioutil.ReadFile("input/4.txt") if err != nil { panic(err) } lines := strings.Split(string(input), "\n") var bestScore = 0.0 var plaintext string // Find the most popular byte in each line for i := 0; i < len(lines)-1; i++ { line, err := hex.DecodeString(lines[i]) if err != nil { log.Println(err) continue } key, err := bytes.Popular(line, 1) if err != nil { log.Println(err) continue } decrypted, err := bytes.XorRepeatingKey(line, key) if err != nil { log.Println(err) continue } // Score each string for how likely it is English decryptedStr := string(decrypted) score := utils.EnglishScore(decryptedStr) if score > bestScore { bestScore = score plaintext = decryptedStr } } return plaintext, expected }
/* Single-byte XOR cipher * Given a string that has been XOR'd against a single character, find * the key and decrypt the string. */ func c3() (actual, expected Result) { input := "1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736" expected = string([]byte{99, 79, 79, 75, 73, 78, 71, 0, 109, 99, 7, 83, 0, 76, 73, 75, 69, 0, 65, 0, 80, 79, 85, 78, 68, 0, 79, 70, 0, 66, 65, 67, 79, 78}) ciphertext, err := hex.DecodeString(input) if err != nil { panic(err) } // Assume the most popular byte in the ciphertext is the key key, err := bytes.Popular(ciphertext, 1) if err != nil { log.Fatal(err) } result, err := bytes.XorRepeatingKey(ciphertext, key) if err != nil { log.Fatal(err) } return string(result), expected }