Substitution cipher

From Rosetta Code
Revision as of 21:27, 26 October 2015 by rosettacode>Zorro1024 (Python implementation)
Substitution 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.

Substitution Cipher Implementation - File Encryption/Decryption

Task - Here we have to do is there will be a input/source file in which we are going to Encrypt the file by replacing every upper/lower case alphabets of the source file with another predetermined upper/lower case alphabets or symbols and save it into another output/encrypted file and then again convert that output/encrypted file into original/decrypted file. This type of Encryption/Decryption scheme is often called a Substitution Cipher. Click here to know more.

See also


C#

<lang csharp>using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;

namespace SubstitutionCipherProject {

   class SubstitutionCipher
   {
       static void Main(string[] args)
       {
           doEncDec("e:\\source.txt", "enc.txt", true);
           doEncDec("enc.txt", "dec.txt", false);
           Console.WriteLine("Done");
           Console.ReadKey();
       }
       static void doEncDec(String source, String target, bool IsEncrypt)
       {
           ITransform trans;
           if (IsEncrypt)
               trans = new Encrypt();
           else
               trans = new Decrypt();
           FileInfo sfi = new FileInfo(source);
           FileStream sstream = sfi.OpenRead();
           StreamReader sr = new StreamReader(sstream);
           FileInfo tfi = new FileInfo(target);
           FileStream tstream = tfi.OpenWrite();
           TransformWriter tw = new TransformWriter(tstream, trans);
           StreamWriter sw = new StreamWriter(tw);
           String line;
           while ((line = sr.ReadLine()) != null)
               sw.WriteLine(line);
           sw.Close();
       }
   }
   public interface ITransform
   {
       byte transform(byte ch);
   }
   public class Encrypt : ITransform
   {
       const String str = "xyfagchbimpourvnqsdewtkjzl";
       byte ITransform.transform(byte ch)
       {
           if (char.IsLower((char)ch))
               ch = (byte)str[ch - (byte)'a'];
           return ch;
       }
   }
   class Decrypt : ITransform
   {
       const String str = "xyfagchbimpourvnqsdewtkjzl";
       byte ITransform.transform(byte ch)
       {
           if (char.IsLower((char)ch))
               ch = (byte)(str.IndexOf((char)ch) + 'a');
           return ch;
       }
   }
   class TransformWriter : Stream, IDisposable
   {
       private Stream outs;
       private ITransform trans;
       public TransformWriter(Stream s, ITransform t)
       {
           this.outs = s;
           this.trans = t;
       }
       public override bool CanRead
       {
           get { return false; }
       }
       public override bool CanSeek
       {
           get { return false; }
       }
       public override bool CanWrite
       {
           get { return true; }
       }
       public override void Flush()
       {
           outs.Flush();
       }
       public override long Length
       {
           get { return outs.Length; }
       }
       public override long Position
       {
           get
           {
               return outs.Position;
           }
           set
           {
               outs.Position = value;
           }
       }
       public override long Seek(long offset, SeekOrigin origin)
       {
           return outs.Seek(offset, origin);
       }
       public override void SetLength(long value)
       {
           outs.SetLength(value);
       }
       public override void Write(byte[] buf, int off, int len)
       {
           for (int i = off; i < off + len; i++)
               buf[i] = trans.transform(buf[i]);
           outs.Write(buf, off, len);
       }
       void IDisposable.Dispose()
       {
           outs.Dispose();
       }
       public override void Close()
       {
           outs.Close();
       }
       public override int Read(byte[] cbuf, int off, int count)
       {
           return outs.Read(cbuf, off, count);
       }
   }

}</lang>

J

Example implementation:

<lang J>keysubst=: [`(a.i.])`(a."_)} key=: 'Taehist' keysubst '!@#$%^&' enc=: a. {~ key i. ] dec=: key {~ a. i. ]

  enc 'This is a test.'

!$%^ %^ @ &#^&.

  dec '!$%^ %^ @ &#^&.'

This is a test.</lang>

Note that this particular implementation bakes the key itself into the implementations of enc and dec. Also note that this particular key is rather limited - letters not mentioned in the key encrypt as another identical character. That seems to be sufficient, given the current task description. But of course other approaches are also possible...

Python

<lang Python> from string import printable import random

EXAMPLE_KEY = .join(sorted(printable, key=lambda _:random.random()))

def encode(plaintext, key):

   return .join(key[printable.index(char)] for char in plaintext)

def decode(plaintext, key):

   return .join(printable[key.index(char)] for char in plaintext)

original = "A simple example." encoded = encode(original, EXAMPLE_KEY) decoded = decode(encoded, EXAMPLE_KEY) print("""The original is: {} Encoding it with the key: {} Gives: {} Decoding it by the same key gives: {}""".format(

   original, EXAMPLE_KEY, encoded, decoded))</lang>

A straightforward implementation. The output is:

<lang>The original is: A simple example. Encoding it with the key: dV1>r7:TLlJa�uY o]MjH\hI^X cPN#!fmv[ <e=04|O'~{y$bAq@}U.WtF*)x/K? Q%S(�RB;25&s6Z9C3+D-_8kn,`Egiwzp"G Gives: iPMhX\YiYmJhX\Y5 Decoding it by the same key gives: A simple example.</lang>

Racket

Uses #REXX input file (in data/substitution.in.txt).

The check-equal? tests assure us that we return the plain text after a cypher/decypher pair; so I don't display the plaintext in the output.

<lang racket>#lang racket/base (require racket/list racket/function racket/file)

(define abc "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")

Used to generate my-key for examples

(define (random-key (alphabet abc))

 (list->string (shuffle (string->list alphabet))))

(define (cypher/decypher key (alphabet abc))

 ;; alist is fine, hashes are better over 40 chars... so alist for
 ;; abc, hash for ASCII.
 (define ab-chars (string->list alphabet))
 (define ky-chars (string->list key))
 (define cypher-alist (map cons ab-chars ky-chars))
 (define decypher-alist (map cons ky-chars ab-chars))
 (define ((subst-map alist) str)
   (list->string (map (lambda (c) (cond [(assoc c alist) => cdr] [else c]))
                      (string->list str))))
 (values (subst-map cypher-alist)
         (subst-map decypher-alist)))

(define (cypher/decypher-files key (alphabet abc))

 (define-values (cypher decypher) (cypher/decypher key alphabet))
 (define ((convert-file f) in out #:exists (exists-flag 'error))
    (curry with-output-to-file out #:exists exists-flag
           (lambda () (display (f (file->string in))))))
 (values (convert-file cypher)
         (convert-file decypher)))

(module+ test

 (require rackunit)
 (define my-key "LXRWzUrIYPJiVQyMwKudbAaDjSEefvhlqmOkGcBZCFsNpxHTgton")
 
 (define-values (cypher decypher) (cypher/decypher my-key abc))
 
 (define in-text #<<T

The quick brown fox... .. jumped over the lazy dog! T

   )
 (define cypher-text (cypher in-text))
 
 (define plain-text (decypher cypher-text))
 (displayln cypher-text)
 (check-equal? plain-text in-text)
 
 (define-values (file-cypher file-decypher) (cypher/decypher-files my-key abc))
 (file-cypher "data/substitution.in.txt" "data/substitution.crypt.txt" #:exists 'replace)
 (file-decypher "data/substitution.crypt.txt" "data/substitution.plain.txt" #:exists 'replace)
 (displayln "---")
 (displayln (file->string "data/substitution.crypt.txt"))
 (check-equal? (file->string "data/substitution.in.txt")
               (file->string "data/substitution.plain.txt")))</lang>
Output:
dmh sHOfG eNCgZ lCt...
.. kHBFhv CThN
xmh cEno vCq!
---
             "zThNo  xCCc  mEp  E  pFhfOlOf  'xmNhpmCcv',
               EZv gmhZ xmh HphN htfhhvp xmh xmNhpmCcv,
           xmh xCCc ehfCBhp E mOZvNEZfh NExmhN xmEZ E mhcF.
          LZo pClxgENh Hphv ehoCZv Oxp xmNhpmCcv gOcc fEHph
              vhfNhEphp OZ FNCvHfxOTOxo, ZCx OZfNhEphp.
       LZv gmhZ OZvOTOvHEc FNCvHfxp ENh FHx xCqhxmhN FOhfhBhEc,
       xmh NhpHcx Op E popxhB xmEx mEp qCZh FEpx Oxp xmNhpmCcv
       ─── E fCHZxhNFNCvHfxOTh fCcchfxOCZ Cl BOplOx HZOxp xmEx
        gCNG EqEOZpx hEfm CxmhN NExmhN xmEZ gOxm hEfm CxmhN."
                               ─── VhNch MENGp

REXX

Programming notes:   the cipher key (as used by this REXX program) is stored in a file as two records:

  •   the 1st record is the plain-text characters to be encrypted.
  •   the 2nd record is the crypt-text characters used for encryption.
  •   the two records should be equal in the number of characters.
  •   the Nth character of record   1   will be encrypted to the Nth character of record   2.

<lang rexx>/*REXX program implements a substitution cipher for all records in a file. */ parse arg fid.1 fid.2 fid.3 fid.4 . /*obtain optional arguments from the CL*/ if fid.1== then fid.1= 'CIPHER.IN' /*Not specified? Then use the default.*/ if fid.2== then fid.2= 'CIPHER.OUT' /* " " " " " " */ if fid.3== then fid.3= 'CIPHER.KEY' /* " " " " " " */ if fid.4== then fid.4= 'CIPHER.ORI' /* " " " " " " */ say ' input file: ' fid.1 /*display the fileID used for input. */ say ' output file: ' fid.2 /* " " " " " output. */ say ' cipher file: ' fid.3 /* " " " " " cipher-key*/ say 'decrypted file: ' fid.4 /* " " " " " decrypted*/ call closer /*close all files in case they're open.*/ say

      do c=1  while lines(fid.3)\==0  /*read (hopefully 2 records) from key. */
      @.c=space(linein(fid.3),0)      /*assign input record to an  @.  array.*/
      end   /*c*/

c=c-1 /*adjust the number of recores (for DO)*/ if c==0 then call ser fid.3, 'not found or is empty.' if c>2 then call ser fid.3, 'has too many records (>2).' if c<2 then call ser fid.3, 'has too few records (<2).' if length(@.1)\==length(@.2) then call ser fid.3, 'has unequal length records.' call encrypt fid.1, fid.2 /*encrypt the input file ───► output.*/ _=@.1; @.1=@.2; @.2=_ /*switch the cipher keys for decryption*/ call encrypt fid.2, fid.4 /*decrypt the output file ───► decrypt.*/ call show 'cipher file ('fid.3")" , fid.3 /*display the cipher-key file*/ call show 'input file ('fid.1")" , fid.1 /* " " input " */ call show 'output file ('fid.2")" , fid.2 /* " " output " */ call show ' decrypted file ('fid.4")" , fid.4 /* " " decrypted " */ exit /*stick a fork in it, we're all done. */ /*────────────────────────────────────────────────────────────────────────────*/ closer: do f=1 for 4; call lineout fid.f; end /*f*/; return ser: say '***error!*** file ' arg(1)" " arg(2); exit show: say; say center(arg(1),79,'═'); "TYPE" arg(2); return /*────────────────────────────────────────────────────────────────────────────*/ encrypt: parse arg @in,@out; 'ERASE' @out /*delete the output file.*/

                            do j=1  while lines(@in)\==0
                            call lineout @out, translate(linein(@in), @.2, @.1)
                            end   /*j*/

j=j-1 /*adjust the number of recores (for DO)*/ if j==0 then call ser @in,'is empty.' /*was the file not found or was empty? */ say @in ' records processed: ' j /*show the number of records processed.*/ call closer /*close all the files to be neat & safe*/ return</lang> output   when using the default input files:

    input file:  CIPHER.IN
   output file:  CIPHER.OUT
   cipher file:  CIPHER.KEY
decrypted file:  CIPHER.ORI

CIPHER.IN  records processed:  10
CIPHER.OUT  records processed:  10

═══════════════════════════cipher file (CIPHER.KEY)════════════════════════════
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
WXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUV

════════════════════════════input file (CIPHER.IN)═════════════════════════════
             "Every  tool  has  a  specific  'threshold',
               and when the user exceeds the threshold,
           the tool becomes a hindrance rather than a help.
          Any software used beyond its threshold will cause
              decreases in productivity, not increases.
       And when individual products are put together piecemeal,
       the result is a system that has gone past its threshold
       ─── a counterproductive collection of misfit units that
        work against each other rather than with each other."
                               ─── Merle Parks

═══════════════════════════output file (CIPHER.OUT)════════════════════════════
             "Aranu  pkkh  dWo  W  olaYebeY  'pdnaodkhZ',
               WjZ sdaj pda qoan atYaaZo pda pdnaodkhZ,
           pda pkkh XaYkiao W dejZnWjYa nWpdan pdWj W dahl.
          wju okbpsWna qoaZ XaukjZ epo pdnaodkhZ sehh YWqoa
              ZaYnaWoao ej lnkZqYperepu, jkp ejYnaWoao.
       wjZ sdaj ejZereZqWh lnkZqYpo Wna lqp pkcapdan leaYaiaWh,
       pda naoqhp eo W ouopai pdWp dWo ckja lWop epo pdnaodkhZ
       ─── W YkqjpanlnkZqYpera YkhhaYpekj kb ieobep qjepo pdWp
        skng WcWejop aWYd kpdan nWpdan pdWj sepd aWYd kpdan."
                               ─── Ianha LWngo

═════════════════════════ decrypted file (CIPHER.ORI)══════════════════════════
             "Every  tool  has  a  specific  'threshold',
               and when the user exceeds the threshold,
           the tool becomes a hindrance rather than a help.
          Any software used beyond its threshold will cause
              decreases in productivity, not increases.
       And when individual products are put together piecemeal,
       the result is a system that has gone past its threshold
       ─── a counterproductive collection of misfit units that
        work against each other rather than with each other."
                               ─── Merle Parks

Tcl

Here we implement a SubCipher class with three public methods:

  • key ?newkey? -- set or return the current substitution key. This validates the key for correspondence with the alphabet, returning an error if something is not right.
  • enc plaintext -- encrypt text with the current key
  • dec ciphertext -- decrypt text with the current key

The default alphabet is a-zA-Z, but can be overridden by providing an argument to the constructor. A random initial key will be generated at construction time, unless that is also provided as an argument.

<lang Tcl>oo::class create SubCipher {

   variable Alphabet
   variable Key
   variable EncMap
   variable DecMap
   constructor {{alphabet abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ} {cipherbet ""}} {
       set Alphabet $alphabet
       if {$cipherbet eq ""} {
           my key [my RandomKey]
       } else {
           my key $cipherbet
       }
   }
   method key args {
       if {$args eq ""} {
           return $Key
       } elseif {[llength $args] > 1} {
           throw {TCL WRONGARGS} "Expected \"[self class] key\" or \"[self class]\" key keystring"
       }
       lassign $args s
       set size [string length $Alphabet]
       if {[string length $s] != $size} {
           return -code error "Key must be $size chars long!"
       }
       set encmap {}
       set decmap {}
       foreach c [split $Alphabet {}] e [split $s {}] {
           dict set encmap $c $e
           dict set decmap $e $c
       }
       if {[dict size $encmap] != $size} {
           return -code error "Alphabet has repeating characters!"
       }
       if {[dict size $decmap] != $size} {
           return -code error "Key has repeating characters!"
       }
       set Key $s
       set EncMap $encmap
       set DecMap $decmap
   }
   method RandomKey {} {
       set chars $Alphabet
       set key {}
       for {set n [string length $chars]} {$n > 0} {incr n -1} {
           set i [expr {int(rand()*$n)}]
           append key [string index $chars $i]
           set chars [string range $chars 0 $i-1][string range $chars $i+1 end]
       }
       return $key
   }
   method enc {s} {
       string map $EncMap $s
   }
   method dec {s} {
       string map $DecMap $s
   }

}</lang>

Testing looks like this:

<lang Tcl>SubCipher create sc set text [read [open /etc/motd]] puts "Original:\n$text\n----\n" puts "Ciphered:\n[set cipher [sc enc $text]]\n----\n" puts "Decrypted:\n[sc dec $cipher]\n----\n" puts "Key:\n[sc key]\n----\n"</lang>

Output:
Original:

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

----

Ciphered:

eVv yzhQzGwm KnLjDFvF lKZV ZVv CvuKGn MgA/rKnDc mJmZvw Gzv Xzvv mhXZlGzv;
ZVv vcGLZ FKmZzKuDZKhn Zvzwm Xhz vGLV yzhQzGw Gzv FvmLzKuvF Kn ZVv
KnFKOKFDGj XKjvm Kn /Dmz/mVGzv/FhL/*/LhyJzKQVZ.

CvuKGn MgA/rKnDc Lhwvm lKZV TbHIrAeWrY gI ETUUTgeY, Zh ZVv vcZvnZ
yvzwKZZvF uJ GyyjKLGujv jGl.

----

Decrypted:

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.

----

Key:
GuLFvXQVKRNjwnhyozmZDOlcJBTbfCWdMkxqprtgIasUHeAiESYP
----