Playfair cipher

From Rosetta Code
Revision as of 04:06, 8 November 2013 by rosettacode>Gerard Schildberger (→‎{{header|REXX}}: changed an error message concerning when the specified Playfair cypher key is too short. -- ~~~~)
Playfair cipher is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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>

  1. 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: BMODZBXDNABEKUDMUIXMMOUVIF

OUTPUT: ========= HI DE TH EG OL DI NT HE TR EX ES TU MP

D

Translation of: Python

<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

Translation of: Perl 6

<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. <lang rexx>/*REXX program implements a PLAYFAIR cypher (encryption & decryption).*/ arg oldK noX _ . '(' oldX if oldK== | oldK==',' then oldK='Playfair example' if noX== | noX==',' then noX='J' if oldX = then oldX='Hide the gold in the tree stump!!' /*default.*/ say ' old cypher=' oldK oldK=scrub(oldK) /*use only Latin alphabet letters*/ newX=scrub(oldX) /* " " " " " */ if _\== then call err 'too many arguments specified.' if newX== then call err 'PHRASE is empty or has no letters' if length(noX)\==1 then call err '"omitted" letter must be only one letter' if length(newK)<3 then call err 'cypher key is too short, must be > 2 character.' if \datatype(noX,'M') then call err '"omitted" letter must be a Latin alphabet letter.' if pos(noX,oldX)\==0 then call err 'PHRASE cant contain the "OMIT" character: ' noX fill=space(translate('ABCDEFGHIJKLMNOPQRSTUVWXYZ',,noX),0) /*remove NOX*/ newK= /* [↓] remove any duplicate chars*/

            do j=1  for length(oldK);            _=substr(oldK,j,1)
            if pos(_,newK)==0  then newK=newK || _
            end   /*j*/

fill=space(translate(fill,,newK),0) /*remove any cypher characters. */ grid=left(newK || fill,26) /*use only the first 25 chars. */ say ' new cypher=' newK say ' old phrase=' oldX say ' new phrase=' newX say ' new digram=' digram(newX)

  1. =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*/

say cText=.playfair(newX, 1); say ' cypher text=' digram(cText) pText=.playfair(cText,0); say ' plain text=' digram(pText) 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 /*──────────────────────────────────SCRUB subroutine────────────────────*/ scrub: procedure; arg stuff /* ARG capitalizes all arguments.*/ $=; do j=1 for length(stuff); _=substr(stuff,j,1)

            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: parse arg T,encrypt; i=-1; if encrypt then i=1; $=

       do k=1  while encrypt;  _=substr(T,k,1);     if _=  then leave
       if _==substr(T,k+1,1)   then T=left(T,k) || 'X' || substr(T,k+1)
       end   /*k*/
 do j=1  by 2  to length(T);    __=strip(substr(T,j,2));    call LR
 if len==1 then __=__ || 'X'; call LR /*append an "X" character, 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  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

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

Tcl

Works with: Tcl version 8.6

<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