Substitution cipher: Difference between revisions

From Rosetta Code
Content added Content deleted
(Tcl implementation added)
Line 354: Line 354:
work against each other rather than with each other."
work against each other rather than with each other."
─── Merle Parks
─── Merle Parks
</pre>

=={{header|Tcl}}==

Here we implement a <tt>SubCipher</tt> class with three public methods:

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

The default alphabet is <tt>a-zA-Z</tt>, 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>

{{out}}
<pre>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
----
</pre>
</pre>

Revision as of 04:21, 19 October 2015

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

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