Playfair cipher
Implement a Playfair cipher encryption and decryption.
The user must be able to choose J = I or no Q in the alphabet. The output of the encrypted and decrypted message must be in capitalized digraphs, separated by spaces.
Output example: HI DE TH EG OL DI NT HE TR EX ES TU MP.
C++
<lang cpp>#include <iostream>
- include <string>
using namespace std;
class playfair { public:
void doIt( string k, string t, bool ij, bool e ) {
createGrid( k, ij ); getTextReady( t, ij, e ); if( e ) doIt( 1 ); else doIt( -1 ); display();
}
private:
void doIt( int dir ) {
int a, b, c, d; string ntxt; for( string::const_iterator ti = _txt.begin(); ti != _txt.end(); ti++ ) { if( getCharPos( *ti++, a, b ) ) if( getCharPos( *ti, c, d ) ) { if( a == c ) { ntxt += getChar( a, b + dir ); ntxt += getChar( c, d + dir ); } else if( b == d ){ ntxt += getChar( a + dir, b ); ntxt += getChar( c + dir, d ); } else { ntxt += getChar( c, b ); ntxt += getChar( a, d ); } } } _txt = ntxt;
}
void display() {
cout << "\n\n OUTPUT:\n=========" << endl; string::iterator si = _txt.begin(); int cnt = 0; while( si != _txt.end() ) { cout << *si; si++; cout << *si << " "; si++; if( ++cnt >= 26 ) cout << endl, cnt = 0; } cout << endl << endl;
}
char getChar( int a, int b ) {
return _m[ (b + 5) % 5 ][ (a + 5) % 5 ];
}
bool getCharPos( char l, int &a, int &b ) {
for( int y = 0; y < 5; y++ ) for( int x = 0; x < 5; x++ ) if( _m[y][x] == l ) { a = x; b = y; return true; }
return false;
}
void getTextReady( string t, bool ij, bool e ) {
for( string::iterator si = t.begin(); si != t.end(); si++ ) { *si = toupper( *si ); if( *si < 65 || *si > 90 ) continue; if( *si == 'J' && ij ) *si = 'I'; else if( *si == 'Q' && !ij ) continue; _txt += *si; } if( e ) { string ntxt = ""; size_t len = _txt.length(); for( size_t x = 0; x < len; x += 2 ) { ntxt += _txt[x]; if( x + 1 < len ) { if( _txt[x] == _txt[x + 1] ) ntxt += 'X'; ntxt += _txt[x + 1]; } } _txt = ntxt; } if( _txt.length() & 1 ) _txt += 'X';
}
void createGrid( string k, bool ij ) {
if( k.length() < 1 ) k = "KEYWORD"; k += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; string nk = ""; for( string::iterator si = k.begin(); si != k.end(); si++ ) { *si = toupper( *si ); if( *si < 65 || *si > 90 ) continue; if( ( *si == 'J' && ij ) || ( *si == 'Q' && !ij ) )continue; if( nk.find( *si ) == -1 ) nk += *si; } copy( nk.begin(), nk.end(), &_m[0][0] );
}
string _txt; char _m[5][5];
};
int main( int argc, char* argv[] ) {
string key, i, txt; bool ij, e; cout << "(E)ncode or (D)ecode? "; getline( cin, i ); e = ( i[0] == 'e' || i[0] == 'E' ); cout << "Enter a en/decryption key: "; getline( cin, key ); cout << "I <-> J (Y/N): "; getline( cin, i ); ij = ( i[0] == 'y' || i[0] == 'Y' ); cout << "Enter the text: "; getline( cin, txt ); playfair pf; pf.doIt( key, txt, ij, e ); return system( "pause" );
}</lang>
- Output:
(E)ncode or (D)ecode? e Enter a en/decryption key: playfair example I <-> J (Y/N): y Enter the text: Hide the gold in the tree stump
OUTPUT: ========= BM OD ZB XD NA BE KU DM UI XM MO UV IF
(E)ncode or (D)ecode? d Enter a en/decryption key: playfair example I <-> J (Y/N): y Enter the text: BMODZBXDNABEKUDMUIXMMOUVIFOUTPUT: ========= HI DE TH EG OL DI NT HE TR EX ES TU MP
D
<lang d>import std.stdio, std.array, std.algorithm, std.range, std.ascii,
std.conv, std.string, std.regex;
string unique(in string s) pure /*nothrow*/ {
string result; foreach (immutable char c; s) if (!result.canFind(c)) result ~= c; return result;
}
struct Playfair {
string from, to; string[string] enc, dec;
this(in string key, in string from_ = "J", in string to_ = null) { this.from = from_; if (to_.empty) this.to = (from_ == "J") ? "I" : "";
auto m = _canonicalize(key ~ uppercase) .unique .chunks(5) .map!text .array; auto I5 = 5.iota;
foreach (const R; m) foreach (immutable i, immutable j; cartesianProduct(I5, I5)) if (i != j) enc[[R[i], R[j]]] = [R[(i + 1) % 5], R[(j+1) % 5]];
foreach (immutable r; I5) { const c = m.transversal(r).array; foreach (immutable i, immutable j; cartesianProduct(I5, I5)) if (i != j) enc[[c[i], c[j]]] = [c[(i + 1) % 5], c[(j+1) % 5]]; }
foreach (i1, j1, i2, j2; cartesianProduct(I5, I5, I5, I5)) if (i1 != i2 && j1 != j2) enc[[m[i1][j1], m[i2][j2]]] = [m[i1][j2], m[i2][j1]];
dec = enc.byValue.zip(enc.byKey).assocArray; }
private string _canonicalize(in string s) const /*pure*/ { return s.toUpper.removechars("^A-Z").replace(from, to); }
string encode(in string s) const { return _canonicalize(s) .matchAll(r"(.)(?:(?!\1)(.))?".regex) //.map!(m => enc[m[0].leftJustify(2, 'X')]) .map!(m => cast()enc[m[0].leftJustify(2, 'X')]) .join(" "); }
string decode(in string s) const /*pure*/ { return _canonicalize(s) .chunks(2) //.map!dec .map!(p => cast()dec[p.text]) .join(" "); }
}
void main() {
const pf = Playfair("Playfair example"); immutable orig = "Hide the gold in...the TREESTUMP!!!"; writeln("Original: ", orig); immutable enc = pf.encode(orig); writeln(" Encoded: ", enc); writeln(" Decoded: ", pf.decode(enc));
}</lang>
- Output:
Original: Hide the gold in...the TREESTUMP!!! Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP
Perl 6
<lang perl6># Instantiate a specific encoder/decoder.
sub playfair( $key,
$from = 'J',
$to = $from eq 'J' ?? 'I' !! ) {
sub canon($str) { $str.subst(/<-alpha>/,, :g).uc.subst(/$from/,$to,:g) }
# Build 5x5 matrix. my @m = canon($key ~ ('A'..'Z').join).comb.uniq.map:
-> $a,$b,$c,$d,$e { [$a,$b,$c,$d,$e] }
# Pregenerate all forward translations. my %ENC = gather {
# Map pairs in same row. for @m -> @r { for ^@r X ^@r -> \i,\j { next if i == j; take @r[i] ~ @r[j] => @r[(i+1)%5] ~ @r[(j+1)%5]; } }
# Map pairs in same column. for ^5 -> $c { my @c = @m.map: *.[$c]; for ^@c X ^@c -> \i,\j { next if i == j; take @c[i] ~ @c[j] => @c[(i+1)%5] ~ @c[(j+1)%5]; } }
# Map pairs with cross-connections. for ^5 X ^5 X ^5 X ^5 -> \i1,\j1,\i2,\j2 { next if i1 == i2 or j1 == j2; take @m[i1][j1] ~ @m[i2][j2] => @m[i1][j2] ~ @m[i2][j1]; }
}
# Generate reverse translations. my %DEC = %ENC.invert;
return
anon sub enc($red) { my @list = canon($red).comb(/(.) (.?) <?{ $1 ne $0 }>/); ~@list.map: { .chars == 1 ?? %ENC{$_~'X'} !! %ENC{$_} } }, anon sub dec($black) { my @list = canon($black).comb(/../); ~@list.map: { %DEC{$_} } } }
my (&encode,&decode) = playfair 'Playfair example';
my $orig = "Hide the gold in...the TREESTUMP!!!"; say " orig:\t$orig";
my $black = encode $orig; say "black:\t$black";
my $red = decode $black; say " red:\t$red";</lang>
- Output:
orig: Hide the gold in...the TREESTUMP!!! black: BM OD ZB XD NA BE KU DM UI XM MO UV IF red: HI DE TH EG OL DI NT HE TR EX ES TU MP
Python
<lang python>from string import ascii_uppercase from itertools import product from re import findall
def uniq(seq):
seen = {} return [seen.setdefault(x, x) for x in seq if x not in seen]
def partition(seq, n):
return [seq[i : i + n] for i in xrange(0, len(seq), n)]
"""Instantiate a specific encoder/decoder."""
def playfair(key, from_ = 'J', to = None):
if to is None: to = 'I' if from_ == 'J' else
def canonicalize(s): return filter(str.isupper, s.upper()).replace(from_, to)
# Build 5x5 matrix. m = partition(uniq(canonicalize(key + ascii_uppercase)), 5)
# Pregenerate all forward translations. enc = {}
# Map pairs in same row. for row in m: for i, j in product(xrange(5), repeat=2): if i != j: enc[row[i] + row[j]] = row[(i + 1) % 5] + row[(j + 1) % 5]
# Map pairs in same column. for c in zip(*m): for i, j in product(xrange(5), repeat=2): if i != j: enc[c[i] + c[j]] = c[(i + 1) % 5] + c[(j + 1) % 5]
# Map pairs with cross-connections. for i1, j1, i2, j2 in product(xrange(5), repeat=4): if i1 != i2 and j1 != j2: enc[m[i1][j1] + m[i2][j2]] = m[i1][j2] + m[i2][j1]
# Generate reverse translations. dec = dict((v, k) for k, v in enc.iteritems())
def sub_enc(txt): lst = findall(r"(.)(?:(?!\1)(.))?", canonicalize(txt)) return " ".join(enc[a + (b if b else 'X')] for a, b in lst)
def sub_dec(encoded): return " ".join(dec[p] for p in partition(canonicalize(encoded), 2))
return sub_enc, sub_dec
(encode, decode) = playfair("Playfair example")
orig = "Hide the gold in...the TREESTUMP!!!"
print "Original:", orig
enc = encode(orig)
print "Encoded:", enc
print "Decoded:", decode(enc)</lang>
- Output:
Original: Hide the gold in...the TREESTUMP!!! Encoded: BM OD ZB XD NA BE KU DM UI XM MO UV IF Decoded: HI DE TH EG OL DI NT HE TR EX ES TU MP
REXX
Quite a bit of the REXX code deals with error checking, accepting arguments, and displaying the options used, and displaying input and output.
Thanks to Walter Pachl, this program is now sensitive of using a suitable double character when x is present in the cipher key.
<lang rexx>/*REXX program implements a PLAYFAIR cipher (encryption & decryption).*/
parse arg key . 1 oldK omit _ . '(' text /*TEXT is the phrase to be used*/
if key == | key ==',' then do; key='Playfair example.'; oldK=key " ◄───using the default."; end
if omit== | omit==',' then omit='J' /*the "omitted" character. */
if text= then text='Hide the gold in the tree stump!!' /*default.*/
newKey =scrub(key ,1) /* " " " " " */
newText=scrub(text) /* " " " " " */
if _\== then call err 'too many arguments specified.'
if newText== then call err 'TEXT is empty or has no letters'
if length(omit)\==1 then call err 'OMIT letter must be only one letter'
if \datatype(omit,'M') then call err 'OMIT letter must be a Latin alphabet letter.'
if pos(omit,newText)\==0 then call err 'TEXT cant contain the "OMIT" character: ' omit
upper omit /*uppercase the OMIT character.*/
fill=space(translate('ABCDEFGHIJKLMNOPQRSTUVWXYZ',,omit),0) /*elide omit*/
xx='X'; if omit==xx then xx='Q' /*char used for double characters*/
if length(newKey)<3 then call err 'cipher key is too short, must be > 2 unique characters.'
fill=space(translate(fill,,newKey),0) /*remove any cipher characters. */
grid=left(newKey || fill,26) /*use only the first 25 chars. */
say ' old cipher: ' strip(oldK) ; padL=14+2; pad=left(,padL)
say ' new cipher: ' newKey ; padX=left(,padL,'═')
say ' omit char: ' omit
say ' double char: ' xx
say ' original text: ' strip(text)
say ' new text: ' newText
- =0
do row =1 for 5 /*build grid (individual cells).*/ do col=1 for 5; #=#+1; @.row.col=substr(grid,#,1) if row==1 then @.0.col=@.1.col if col==5 then do; @.row.6=@.row.1; @.row.0=@.row.5; end if row==5 then do; @.6.col=@.1.col; @.0.col=@.5.col; end end /*col*/ end /*row*/
cText=.Playfair(newText, 1); call show 'cipher' , cText pText=.Playfair(cText ); call show 'plain' , pText qText=space(translate(pText,,xx),0) /*remove char used for "doubles."*/ if length(qText)\==length(pText) then call show 'possible', qText say ' original text: ' newText; say /*··· and show the original text.*/ if qtext==newText then say padx 'Playfair encryption─►decryption─►encryption worked.' exit /*stick a fork in it, we're done.*/ /*──────────────────────────────────one─line subroutines───────────────────────────*/ @@: parse arg Xrow,Xcol; return @.Xrow.Xcol err: say; say '***error!***' arg(1); say; exit 13 LR: rowL=row(left(__,1)); colL=_; rowR=row(right(__,1)); colR=_; return length(__) row: ?=pos(arg(1),grid); _=(?-1)//5+1; return (4+?)%5 show: arg x,y; say; say right(x 'text: ',padL) digram(y); say pad space(y,0); return /*──────────────────────────────────SCRUB subroutine────────────────────*/ scrub: procedure; arg xxx,unique; xxx=space(xxx,0) /*ARG caps all args*/ $=; do j=1 for length(xxx); _=substr(xxx,j,1)
if unique==1 then if pos(_,$)\==0 then iterate /*unique?*/ if datatype(_,'M') then $=$||_ /*only use Latin letters. */ end /*j*/
return $ /*──────────────────────────────────DIGRAM subroutine───────────────────*/ digram: procedure; parse arg x; $=; do j=1 by 2 to length(x)
$=$ || substr(x,j,2)' ' end /*j*/
return strip($) /*──────────────────────────────────.PLAYFAIR subroutine────────────────*/ .Playfair: arg T,encrypt; i=-1; if encrypt==1 then i=1; $=
do k=1 while i==1; _=substr(T,k,1); if _==' ' then leave if _==substr(T,k+1 ,1) then T=left(T,k) || xx || substr(T,k+1) end /*k*/ do j=1 by 2 to length(T); __=strip(substr(T,j,2)) if LR()==1 then __=__ || xx; call LR /*append X or Q char, rule 1*/ select when rowL==rowR then __=@@(rowL, colL+i)@@(rowR, colR+i) /*rule 2*/ when colL==colR then __=@@(rowL+i,colL )@@(rowR+i,colR) /*rule 3*/ otherwise __=@@(rowL, colR )@@(rowR, colL) /*rule 4*/ end /*select*/ $=$ || __ end /*j*/
return $</lang> output when using the default inputs:
old cipher: Playfair example. ◄───using the default. new cipher: PLAYFIREXM omit char: J double char: X original text: Hide the gold in the tree stump!! new text: HIDETHEGOLDINTHETREESTUMP CIPHER text: BM OD ZB XD NA BE KU DM UI XM MO UV IF BMODZBXDNABEKUDMUIXMMOUVIF PLAIN text: HI DE TH EG OL DI NT HE TR EX ES TU MP HIDETHEGOLDINTHETREXESTUMP POSSIBLE text: HI DE TH EG OL DI NT HE TR EE ST UM P HIDETHEGOLDINTHETREESTUMP original text: HIDETHEGOLDINTHETREESTUMP ════════════════ Playfair encryption─►decryption─►encryption worked.
After the usual replacements for $, @, #, and x= I ran the program on ooRexx with the following correct results:
old cypher= Playfair example new cypher= PLAYFIREXM old phrase= Hide the gold in the tree stump!! new phrase= HIDETHEGOLDINTHETREESTUMP new digram= HI DE TH EG OL DI NT HE TR EE ST UM P cypher text= BM OD ZB XD NA BE KU DM UI XM MO UV IF plain text= HI DE TH EG OL DI NT HE TR EX ES TU MP
output when using the input of: stuvw x (myteest
old cipher: stuvw new cipher: STUVW omit char: X double char: Q original text: myteest new text: MYTEEST CIPHER text: NR WB ZB TU NRWBZBTU PLAIN text: MY TE QE ST MYTEQEST POSSIBLE text: MY TE ES T MYTEEST original text: MYTEEST ════════════════ Playfair encryption─►decryption─►encryption worked.
Tcl
<lang tcl>package require TclOO
oo::class create Playfair {
variable grid lookup excluder constructor {{keyword "PLAYFAIR EXAMPLE"} {exclude "J"}} {
# Tweaking according to exact operation mode if {$exclude eq "J"} { set excluder "J I" } else { set excluder [list $exclude ""] }
# Clean up the keyword source set keys [my Clean [append keyword "ABCDEFGHIJKLMNOPQRSTUVWXYZ"]]
# Generate the encoding grid set grid [lrepeat 5 [lrepeat 5 ""]] set idx -1 for {set i 0} {$i < 5} {incr i} {for {set j 0} {$j < 5} {} { if {![info exist lookup([set c [lindex $keys [incr idx]]])]} { lset grid $i $j $c set lookup($c) [list $i $j] incr j } }}
# Sanity check if {[array size lookup] != 25} { error "failed to build encoding table correctly" }
}
# Worker to apply a consistent cleanup/split rule method Clean {str} {
set str [string map $excluder [string toupper $str]] split [regsub -all {[^A-Z]} $str ""] ""
}
# These public methods are implemented by a single non-public method forward encode my Transform 1 forward decode my Transform -1
# The application of the Playfair cypher transform method Transform {direction message} {
# Split message into true digraphs foreach c [my Clean $message] { if {![info exists lookup($c)]} continue if {[info exist c0]} { lappend digraphs $c0 [expr {$c0 eq $c ? "X" : $c}] unset c0 } else { set c0 $c } } if {[info exist c0]} { lappend digraphs $c0 "Z" }
# Encode the digraphs set result "" foreach {a b} $digraphs { lassign $lookup($a) ai aj lassign $lookup($b) bi bj if {$ai == $bi} { set aj [expr {($aj + $direction) % 5}] set bj [expr {($bj + $direction) % 5}] } elseif {$aj == $bj} { set ai [expr {($ai + $direction) % 5}] set bi [expr {($bi + $direction) % 5}] } else { set tmp $aj set aj $bj set bj $tmp } lappend result [lindex $grid $ai $aj][lindex $grid $bi $bj] }
# Real use would be: return [join $result ""] return $result
}
}</lang> Demonstrating: <lang tcl>Playfair create cypher "Playfair Example" set plaintext "Hide the gold in...the TREESTUMP!!!" set encoded [cypher encode $plaintext] set decoded [cypher decode $encoded] puts "Original: $plaintext" puts "Encoded: $encoded" puts "Decoded: $decoded"</lang>
- Output:
Original: Hide the gold in...the TREESTUMP!!! Encoded: BM OD ZB XD NA BE KU DM UI XM KZ ZR FT Decoded: HI DE TH EG OL DI NT HE TR EX ST UM PZ