Playfair cipher: Difference between revisions

From Rosetta Code
Content added Content deleted
m (→‎{{header|REXX}}: showed the cleansed text in plaintext and digram versions. -- ~~~~)
Line 398: Line 398:
end /*row*/
end /*row*/


eText=.Playfair(newText, 1); call show 'encypted' , eText
eText=.Playfair(newText, 1); call show 'encrypted' , eText
pText=.Playfair(eText ); call show 'plain' , pText
pText=.Playfair(eText ); call show 'plain' , pText
qText=changestr(xx ||xx,pText,Lxx) /*change doubled doublechar─►sing*/
qText=changestr(xx ||xx,pText,Lxx) /*change doubled doublechar─►sing*/
Line 453: Line 453:
HIDETHEGOLDINTHETREESTUMP
HIDETHEGOLDINTHETREESTUMP


encypted text: BM OD ZB XD NA BE KU DM UI XM MO UV IF
encrypted text: BM OD ZB XD NA BE KU DM UI XM MO UV IF
BMODZBXDNABEKUDMUIXMMOUVIF
BMODZBXDNABEKUDMUIXMMOUVIF


Line 476: Line 476:
MYTEEST
MYTEEST


encypted text: NR WB ZB TU
encrypted text: NR WB ZB TU
NRWBZBTU
NRWBZBTU



Revision as of 10:10, 10 November 2013

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, and displaying input and output.
For ease of viewing and comparing, the output is in capitalized digraphs (which are really digrams) as well as the original input(s).
Thanks to Walter Pachl, this program is now sensitive of using a suitable double character when   X   is present in the cipher key.
Also added a a fair amount of code to massage the decrypted encryption to remove doubled   Xes   so as to match the original text. <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) /*scrub old cipher key──► newKey */ newText=scrub(text ) /* " " text ──► newText*/ 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.' @abc='abcdefghijklmnopqrstuvwxyz'; @abcU=@abc upper omit @abcU /*uppercase OMIT char & alphabet.*/ if pos(omit,newText)\==0 then call err 'TEXT cant contain the "OMIT" character: ' omit fill=space(translate(@abcU,, omit), 0) /*elide OMIT char from alphabet. */ 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 ≥3 unique characters.' fill=space(translate(fill,,newKey),0) /*remove any cipher characters. */ grid=newKey || fill /*only first 25 chars are used.*/ say 'old cipher key: ' strip(oldK)  ; padL=14+2; pad=left(,padL) say 'new cipher key: ' newKey  ; padX=left(,padL,"═")'Playfair' say ' omit char: ' omit /* [↓] lowercase of double char.*/ say ' double char: ' xx  ; Lxx=translate(xx, @abc, @abcU) say ' original text: ' strip(text) /* [↓] doubled version of Lxx. */ call show 'cleansed', newText  ; LxxLxx=Lxx || Lxx

  1. =0 /*number of grid characters used.*/
          do row   =1  for 5          /*build array of 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*/

eText=.Playfair(newText, 1); call show 'encrypted' , eText pText=.Playfair(eText ); call show 'plain' , pText qText=changestr(xx ||xx,pText,Lxx) /*change doubled doublechar─►sing*/ qText=changestr(Lxx||xx,qText,LxxLxx) /*change Xx ──► lowercase dblChar*/ qText=space(translate(qText,,xx),0) /*remove char used for "doubles."*/ upper qText /*reinstate the use of upperchars*/ 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 '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 ,y; say; say right(arg(1) '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) || Lxx || substr(T,k+1)
       end   /*k*/

upper T

 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 key:  Playfair example.    ◄───using the default.
new cipher key:  PLAYFIREXM
     omit char:  J
   double char:  X
 original text:  Hide the gold in the tree stump!!

 cleansed text:  HI DE TH EG OL DI NT HE TR EE ST UM P
                 HIDETHEGOLDINTHETREESTUMP

encrypted 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. 

output when using the input of:   stuvw x (myteest

old cipher key:  stuvw
new cipher key:  STUVW
     omit char:  X
   double char:  Q
 original text:  myteest

 cleansed text:  MY TE ES T
                 MYTEEST

encrypted 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

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