Task: Playfair cipher




Go: package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"
)

type playfairOption int

const (
    noQ playfairOption = iota
    iEqualsJ
)

type playfair struct {
    keyword string
    pfo     playfairOption
    table   [5][5]byte
}

func (p *playfair) init() {
    
    var used [26]bool 
    if p.pfo == noQ {
        used[16] = true 
    } else {
        used[9] = true 
    }
    alphabet := strings.ToUpper(p.keyword) + "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    for i, j, k := 0, 0, 0; k < len(alphabet); k++ {
        c := alphabet[k]
        if c < 'A' || c > 'Z' {
            continue
        }
        d := int(c - 65)
        if !used[d] {
            p.table[i][j] = c
            used[d] = true
            j++
            if j == 5 {
                i++
                if i == 5 {
                    break 
                }
                j = 0
            }
        }
    }
}

func (p *playfair) getCleanText(plainText string) string {
    
    plainText = strings.ToUpper(plainText)
    
    var cleanText strings.Builder
    
    prevByte := byte('\000')
    for i := 0; i < len(plainText); i++ {
        nextByte := plainText[i]
        
        
        if nextByte < 'A' || nextByte > 'Z' || (nextByte == 'Q' && p.pfo == noQ) {
            continue
        }
        
        if nextByte == 'J' && p.pfo == iEqualsJ {
            nextByte = 'I'
        }
        if nextByte != prevByte {
            cleanText.WriteByte(nextByte)
        } else {
            cleanText.WriteByte('X')
            cleanText.WriteByte(nextByte)
        }
        prevByte = nextByte
    }
    l := cleanText.Len()
    if l%2 == 1 {
        
        if cleanText.String()[l-1] != 'X' {
            cleanText.WriteByte('X')
        } else {
            cleanText.WriteByte('Z')
        }
    }
    return cleanText.String()
}

func (p *playfair) findByte(c byte) (int, int) {
    for i := 0; i < 5; i++ {
        for j := 0; j < 5; j++ {
            if p.table[i][j] == c {
                return i, j
            }
        }
    }
    return -1, -1
}

func (p *playfair) encode(plainText string) string {
    cleanText := p.getCleanText(plainText)
    var cipherText strings.Builder
    l := len(cleanText)
    for i := 0; i < l; i += 2 {
        row1, col1 := p.findByte(cleanText[i])
        row2, col2 := p.findByte(cleanText[i+1])
        switch {
        case row1 == row2:
            cipherText.WriteByte(p.table[row1][(col1+1)%5])
            cipherText.WriteByte(p.table[row2][(col2+1)%5])
        case col1 == col2:
            cipherText.WriteByte(p.table[(row1+1)%5][col1])
            cipherText.WriteByte(p.table[(row2+1)%5][col2])
        default:
            cipherText.WriteByte(p.table[row1][col2])
            cipherText.WriteByte(p.table[row2][col1])
        }
        if i < l-1 {
            cipherText.WriteByte(' ')
        }
    }
    return cipherText.String()
}

func (p *playfair) decode(cipherText string) string {
    var decodedText strings.Builder
    l := len(cipherText)
    
    for i := 0; i < l; i += 3 {
        row1, col1 := p.findByte(cipherText[i])
        row2, col2 := p.findByte(cipherText[i+1])
        switch {
        case row1 == row2:
            temp := 4
            if col1 > 0 {
                temp = col1 - 1
            }
            decodedText.WriteByte(p.table[row1][temp])
            temp = 4
            if col2 > 0 {
                temp = col2 - 1
            }
            decodedText.WriteByte(p.table[row2][temp])
        case col1 == col2:
            temp := 4
            if row1 > 0 {
                temp = row1 - 1
            }
            decodedText.WriteByte(p.table[temp][col1])
            temp = 4
            if row2 > 0 {
                temp = row2 - 1
            }
            decodedText.WriteByte(p.table[temp][col2])
        default:
            decodedText.WriteByte(p.table[row1][col2])
            decodedText.WriteByte(p.table[row2][col1])
        }
        if i < l-1 {
            decodedText.WriteByte(' ')
        }
    }
    return decodedText.String()
}

func (p *playfair) printTable() {
    fmt.Println("The table to be used is :\n")
    for i := 0; i < 5; i++ {
        for j := 0; j < 5; j++ {
            fmt.Printf("%c ", p.table[i][j])
        }
        fmt.Println()
    }
}

func main() {
    scanner := bufio.NewScanner(os.Stdin)
    fmt.Print("Enter Playfair keyword : ")
    scanner.Scan()
    keyword := scanner.Text()
    var ignoreQ string
    for ignoreQ != "y" && ignoreQ != "n" {
        fmt.Print("Ignore Q when building table  y/n : ")
        scanner.Scan()
        ignoreQ = strings.ToLower(scanner.Text())
    }
    pfo := noQ
    if ignoreQ == "n" {
        pfo = iEqualsJ
    }
    var table [5][5]byte
    pf := &playfair{keyword, pfo, table}
    pf.init()
    pf.printTable()
    fmt.Print("\nEnter plain text : ")
    scanner.Scan()
    plainText := scanner.Text()
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading standard input:", err)
        return
    }
    encodedText := pf.encode(plainText)
    fmt.Println("\nEncoded text is :", encodedText)
    decodedText := pf.decode(encodedText)
    fmt.Println("Deccoded text is :", decodedText)
}


Java: import java.awt.Point;
import java.util.Scanner;

public class PlayfairCipher {
    private static char[][] charTable;
    private static Point[] positions;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        String key = prompt("Enter an encryption key (min length 6): ", sc, 6);
        String txt = prompt("Enter the message: ", sc, 1);
        String jti = prompt("Replace J with I? y/n: ", sc, 1);

        boolean changeJtoI = jti.equalsIgnoreCase("y");

        createTable(key, changeJtoI);

        String enc = encode(prepareText(txt, changeJtoI));

        System.out.printf("%nEncoded message: %n%s%n", enc);
        System.out.printf("%nDecoded message: %n%s%n", decode(enc));
    }

    private static String prompt(String promptText, Scanner sc, int minLen) {
        String s;
        do {
            System.out.print(promptText);
            s = sc.nextLine().trim();
        } while (s.length() < minLen);
        return s;
    }

    private static String prepareText(String s, boolean changeJtoI) {
        s = s.toUpperCase().replaceAll("[^A-Z]", "");
        return changeJtoI ? s.replace("J", "I") : s.replace("Q", "");
    }

    private static void createTable(String key, boolean changeJtoI) {
        charTable = new char[5][5];
        positions = new Point[26];

        String s = prepareText(key + "ABCDEFGHIJKLMNOPQRSTUVWXYZ", changeJtoI);

        int len = s.length();
        for (int i = 0, k = 0; i < len; i++) {
            char c = s.charAt(i);
            if (positions[c - 'A'] == null) {
                charTable[k / 5][k % 5] = c;
                positions[c - 'A'] = new Point(k % 5, k / 5);
                k++;
            }
        }
    }

    private static String encode(String s) {
        StringBuilder sb = new StringBuilder(s);

        for (int i = 0; i < sb.length(); i += 2) {

            if (i == sb.length() - 1)
                sb.append(sb.length() % 2 == 1 ? 'X' : "");

            else if (sb.charAt(i) == sb.charAt(i + 1))
                sb.insert(i + 1, 'X');
        }
        return codec(sb, 1);
    }

    private static String decode(String s) {
        return codec(new StringBuilder(s), 4);
    }

    private static String codec(StringBuilder text, int direction) {
        int len = text.length();
        for (int i = 0; i < len; i += 2) {
            char a = text.charAt(i);
            char b = text.charAt(i + 1);

            int row1 = positions[a - 'A'].y;
            int row2 = positions[b - 'A'].y;
            int col1 = positions[a - 'A'].x;
            int col2 = positions[b - 'A'].x;

            if (row1 == row2) {
                col1 = (col1 + direction) % 5;
                col2 = (col2 + direction) % 5;

            } else if (col1 == col2) {
                row1 = (row1 + direction) % 5;
                row2 = (row2 + direction) % 5;

            } else {
                int tmp = col1;
                col1 = col2;
                col2 = tmp;
            }

            text.setCharAt(i, charTable[row1][col1]);
            text.setCharAt(i + 1, charTable[row2][col2]);
        }
        return text.toString();
    }
}