new(transposition): Implementation of simple column transposition cipher.
authorsgf <sgf.dma@gmail.com>
Tue, 5 Dec 2023 20:56:13 +0000 (23:56 +0300)
committersgf <sgf.dma@gmail.com>
Tue, 5 Dec 2023 20:56:13 +0000 (23:56 +0300)
trans-cipher/.gitignore [new file with mode: 0644]
trans-cipher/main.go [new file with mode: 0644]
trans-cipher/main_test.go [new file with mode: 0644]

diff --git a/trans-cipher/.gitignore b/trans-cipher/.gitignore
new file mode 100644 (file)
index 0000000..d848d0c
--- /dev/null
@@ -0,0 +1,2 @@
+[0-9].cpp
+a.out
diff --git a/trans-cipher/main.go b/trans-cipher/main.go
new file mode 100644 (file)
index 0000000..0ad6312
--- /dev/null
@@ -0,0 +1,120 @@
+
+package main
+
+import (
+    "fmt"
+)
+
+// Encrypt message using columnar transposition:
+// 1. Write text in n*m matrix row by row.
+// 2. Then read message column by column. The order of columns to read is
+//    determined by key.
+//
+// This is similar to https://www.boxentriq.com/code-breaking/columnar-transposition-cipher ,
+// except that if message does not fit into integer number of n*m matrixes,
+// here i'll pad the message.
+//
+// Encoding/decoding is done matrix by matrix.
+
+func encryption[T any](s []T, n, m int, key []int, pad T) []T {
+    var enc []T
+
+    // Matrix size.
+    size := n*m
+    if len(s)%size == 0 {
+        enc = make([]T, len(s))
+    } else {
+        enc = make([]T, len(s) + (size - len(s)%size))
+    }
+    fmt.Printf("enc len = %v, size = %v\n", len(enc), size)
+
+    // mx - matrix number, i - index inside the given matrix.
+    enc_elem := func (mx, i int, c T) {
+        bl  := key[i%n] - 1     // m-block number
+        pos := i/n              // position in m-block
+        res := mx*size + bl*m + pos
+        //fmt.Printf("[%v]: '%c' --> mx: %v, bl: %v, pos: %v, res: %v\n", i, c, mx, bl, pos, res)
+        enc[res] = c
+    }
+
+    // Each matrix contains 'size' elements.
+    enc_matrix := func(i int, c T) { enc_elem(i/size, i%size, c) }
+
+    for i := 0; i < len(s); i++ {
+        enc_matrix(i, s[i])
+    }
+    // Padding
+    for i := len(s); i < len(enc); i++ {
+        enc_matrix(i, pad)
+    }
+
+    return enc
+}
+
+func decryption[T any](s []T, n, m int, key []int) []T {
+    var dec []T
+
+    size := n*m
+    if len(s)%size == 0 {
+        dec = make([]T, len(s))
+    } else {
+        dec = make([]T, len(s) + (size - len(s)%size))
+    }
+
+    inv_key := make([]int, len(key))
+    for i := 0; i < len(key); i ++ {
+        inv_key[key[i]-1] = i
+    }
+    //fmt.Printf("inv key = %v\n", inv_key)
+
+    // mx - matrix number, i - index inside the given matrix.
+    dec_elem := func(mx, i int, c T) {
+        col := inv_key[i/m]     // column
+        row := i%m              // row
+        res := mx*size + row*n + col
+        //fmt.Printf("[%v]: '%v' --> mx: %v, col: %v, row: %v, res: %v\n", i, c, mx, col, row, res)
+        dec[res] = c
+    }
+
+    dec_matrix := func(i int, c T) { dec_elem(i/size, i%size, c) }
+
+    for i, c := range(s) {
+        dec_matrix(i, c)
+    }
+
+    return dec
+}
+
+func main() {
+    var n, m int
+    fmt.Printf("n m: ");
+    if _, err := fmt.Scanln(&n, &m); err != nil {
+        panic(err)
+    }
+    fmt.Printf("Read n=%v m=%v\n", n, m)
+
+    key := make([]int, 0)
+    fmt.Printf("Key: ")
+    for i := 0; i < n; i++ {
+        var v int
+        if _, err := fmt.Scan(&v); err != nil {
+            break
+        }
+        key = append(key, v)
+    }
+    fmt.Printf("Read key=%v\n", key);
+
+    var txt string
+    fmt.Printf("Text:")
+    if _, err := fmt.Scanf("%s", &txt); err != nil {
+        panic(err)
+    }
+    fmt.Printf("Read text=%v\n", txt);
+
+    enc := encryption([]byte(txt), n, m, key, '_')
+    fmt.Printf("enc = %v\n", string(enc))
+    dec := decryption(enc, n, m, key)
+    //dec := decryption([]byte(txt), n, m, key)
+    fmt.Printf("dec = %v\n", string(dec))
+}
+
diff --git a/trans-cipher/main_test.go b/trans-cipher/main_test.go
new file mode 100644 (file)
index 0000000..86b4594
--- /dev/null
@@ -0,0 +1,93 @@
+
+package main
+
+import (
+    "fmt"
+    "testing"
+    "bytes"
+    "strings"
+)
+
+type genKey struct {
+    i int   // current swap index
+    n int   // done permutations
+    max int // max number of permutations
+    key []int
+}
+
+func (g *genKey) init(key []int) {
+    g.key = key
+
+    g.max = 1
+    for t := 1; t <= len(key); t++ {
+        g.max = g.max * t
+    }
+}
+
+func (g *genKey) next() bool {
+    if g.n == 0 {
+        g.n++
+        return true
+    }
+
+    if g.n >= g.max {
+        return false
+    }
+
+    g.n++
+    j := g.i + 1
+    if j >= len(g.key) {
+        j = 0
+    }
+    g.key[g.i], g.key[j] = g.key[j], g.key[g.i]
+
+    g.i++
+    if (g.i >= len(g.key)) {
+        g.i = 0
+    }
+
+    return true
+}
+
+func TestEncDec(t *testing.T) {
+
+    run := func(n, m, l int) {
+        var buf bytes.Buffer
+        for i := 1; i <= l; i++ {
+            fmt.Fprintf(&buf, "%d", i%10)
+        }
+
+        var g genKey
+        key := make([]int, 0, n)
+        for i := 1; i <= n; i++ {
+            key = append(key, i)
+        }
+        g.init(key)
+
+        for g.next() {
+            bs := buf.Bytes()
+            fmt.Printf("Testing %vx%v: '%v' (%v) with %v\n", n, m, string(bs), l, key)
+
+            enc := encryption(bs, n, m, key, '_')
+            fmt.Printf("Encrypted: %v\n", string(enc))
+            //dec := decryption(enc, n, m, key)
+            dec := decryption([]byte(strings.TrimRight(string(enc), "_")), n, m, key)
+            for i := 0; i < len(bs); i++ {
+                if bs[i] != dec[i] {
+                    t.Errorf("Failed at %v: %c != %c\n", i, bs[i], dec[i])
+                }
+            }
+        }
+    }
+
+    for n := 1; n < 4; n++ {
+        for m := 1; m < 4; m++ {
+            for l := (n*m)/2; l <= n*m + (n*m)/2; l++ {
+            //for l := n*m-1; l <= n*m; l++ {
+                run(n, m, l)
+            }
+        }
+    }
+
+}
+