Pick a depth. Each prompt opens in your AI pre-loaded with the lesson. Click a row to preview the prompt.
Transposition ciphers don't change which letters appear in the message — only their order. By itself this is weak (a cryptanalyst can recover the plaintext by anagramming and language statistics), but transposition is half of what makes modern block ciphers work. Shannon identified the two components every strong cipher needs: confusion (a complex relationship between key and ciphertext, achieved by substitution) and diffusion (each plaintext bit affecting many ciphertext bits, achieved by transposition). AES alternates ShiftRows and MixColumns (transposition / diffusion) with SubBytes (substitution / confusion) for ten rounds. So when you study a Rail Fence or columnar transposition, you are studying the half of AES that spreads information around — without it, even strong substitution would leak local plaintext structure.
A columnar transposition writes the plaintext into a grid row by row under a key that orders the columns, then reads off the columns in the order specified by the key. The Rail Fence cipher is a special case where the 'columns' are zigzag rails.
// main.go
package main
import (
"fmt"
"sort"
"strings"
)
func railFenceEncrypt(plaintext string, rails int) string {
plaintext = strings.ReplaceAll(plaintext, " ", "")
fence := make([][]byte, rails)
rail, step := 0, 1
for i := 0; i < len(plaintext); i++ {
fence[rail] = append(fence[rail], plaintext[i])
rail += step
if rail == 0 || rail == rails-1 {
step = -step
}
}
var sb strings.Builder
for _, r := range fence {
sb.Write(r)
}
return sb.String()
}
func columnarEncrypt(plaintext, key string) string {
pt := strings.ToUpper(strings.ReplaceAll(plaintext, " ", ""))
cols := len(key)
rows := (len(pt) + cols - 1) / cols
// Pad with X
for len(pt) < rows*cols {
pt += "X"
}
// Build column order sorted by key letter
order := make([]int, cols)
for i := range order {
order[i] = i
}
sort.Slice(order, func(a, b int) bool { return key[order[a]] < key[order[b]] })
var sb strings.Builder
for _, c := range order {
for r := 0; r < rows; r++ {
sb.WriteByte(pt[r*cols+c])
}
}
return sb.String()
}
func columnarDecrypt(ciphertext, key string) string {
cols := len(key)
rows := len(ciphertext) / cols
order := make([]int, cols)
for i := range order {
order[i] = i
}
sort.Slice(order, func(a, b int) bool { return key[order[a]] < key[order[b]] })
grid := make([][]byte, rows)
for i := range grid {
grid[i] = make([]byte, cols)
}
pos := 0
for _, c := range order {
for r := 0; r < rows; r++ {
grid[r][c] = ciphertext[pos]
pos++
}
}
var sb strings.Builder
for _, row := range grid {
sb.Write(row)
}
return sb.String()
}
func main() {
fmt.Println(railFenceEncrypt("WEAREDISCOVEREDFLEEATONCE", 3))
// WECRLTEERDSOEEFEAOCAIVDEN
ct := columnarEncrypt("WEAREDISCOVEREDFLEEATONCE", "ZEBRA")
fmt.Println(ct)
fmt.Println(columnarDecrypt(ct, "ZEBRA"))
}go run main.go