Substitution cipher

From Rosetta Code
Revision as of 15:42, 30 October 2016 by rosettacode>FreeTom (→‎{{header|Lua}}: fixed spacing)
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


Ada

<lang ada> with Ada.Command_Line; use Ada.Command_Line; with Ada.Sequential_IO; with Ada.Strings.Maps; use Ada.Strings.Maps; with Ada.Text_IO;

procedure Cipher is

  package Char_IO is new Ada.Sequential_IO (Character);
  use Char_IO;
  Alphabet: constant String := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  Key :     constant String := "VsciBjedgrzyHalvXZKtUPumGfIwJxqOCFRApnDhQWobLkESYMTN";
  My_Map : Character_Mapping;
  Input, Output : File_Type;
  Buffer        : Character;

begin

  declare
     use Ada.Text_IO;
  begin
     if Argument_Count /= 1 then

Put_Line("Usage: " & Command_Name & " <encode|decode>");

     else

if Argument(1) = "encode" then My_Map := To_Mapping(From => Alphabet, To => Key); elsif Argument(1) = "decode" then My_Map := To_Mapping(From => Key, To => Alphabet); else Put_Line("Unrecognised Argument: " & Argument(1)); return; end if;

     end if;
  end;
  Open       (File => Input,  Mode => In_File,  Name => "input.txt");
  Create     (File => Output, Mode => Out_File, Name => "output.txt");
  loop
     Read  (File => Input,  Item => Buffer);
     Buffer := Value(Map => My_Map, Element => Buffer);
     Write (File => Output, Item => Buffer);
  end loop;

exception

  when Char_IO.End_Error =>
     if Is_Open(Input) then
        Close (Input);
     end if;
     if Is_Open(Output) then
        Close (Output);
     end if;

end Cipher; </lang>

C++

The key file should look something like this: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789SWoVR0kJLXQ8zbCd1OagTH5ie3nvYU2wfrM9yI4sKm6c7hNjtADqFPxpEZlBuG <lang cpp>

  1. include <iostream>
  2. include <string>
  3. include <fstream>

class cipher { public:

   bool work( std::string e, std::string f, std::string k ) {
       if( e.length() < 1 ) return false;
       fileBuffer = readFile( f );
       if( "" == fileBuffer ) return false;
       keyBuffer = readFile( k );
       if( "" == keyBuffer ) return false;
       outName = f;
       outName.insert( outName.find_first_of( "." ), "_out" );
       switch( e[0] ) {
           case 'e': return encode();
           case 'd': return decode();
       }
       return false;
   }

private:

   bool encode() {
       size_t idx, len = keyBuffer.length() >> 1;
       for( std::string::iterator i = fileBuffer.begin(); i != fileBuffer.end(); i++ ) {
           idx = keyBuffer.find_first_of( *i );
           if( idx < len ) outBuffer.append( 1, keyBuffer.at( idx + len ) );
           else outBuffer.append( 1, *i );
       }
       return saveOutput();
   }
   bool decode() {
       size_t idx, l = keyBuffer.length(), len = l >> 1;
       for( std::string::iterator i = fileBuffer.begin(); i != fileBuffer.end(); i++ ) {
           idx = keyBuffer.find_last_of( *i );
           if( idx >= len && idx < l ) outBuffer.append( 1, keyBuffer.at( idx - len ) );
           else outBuffer.append( 1, *i );
       }
       return saveOutput();
   }
   bool saveOutput() {
       std::ofstream o( outName.c_str() );
       o.write( outBuffer.c_str(), outBuffer.size() );
       o.close();
       return true;
   }
   std::string readFile( std::string fl ) {
       std::string buffer = "";
       std::ifstream f( fl.c_str(), std::ios_base::in );
       if( f.good() ) {
           buffer = std::string( ( std::istreambuf_iterator<char>( f ) ), std::istreambuf_iterator<char>() );
           f.close();
       }
       return buffer;
   }
   std::string fileBuffer, keyBuffer, outBuffer, outName;

};

int main( int argc, char* argv[] ) {

   if( argc < 4 ) {
       std::cout << "<d or e>\tDecrypt or Encrypt\n<filename>\tInput file, the output file will have"
       "'_out' added to it.\n<key>\t\tfile with the key to encode/decode\n\n";
   } else {
       cipher c;
       if( c.work( argv[1], argv[2], argv[3] ) ) std::cout << "\nFile successfully saved!\n\n";
       else std::cout << "Something went wrong!\n\n";
   }
   return 0;

} </lang>

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>

Fortran

Works with: Fortran version 90 and later

<lang fortran>program substitution

 implicit none
 integer, parameter :: len_max = 256
 integer, parameter :: eof = -1
 integer :: in_unit = 9, out_unit = 10, ios
 character(len_max) :: line
 
 open(in_unit, file="plain.txt",  iostat=ios)
 if (ios /= 0) then
   write(*,*) "Error opening plain.txt file"
   stop
 end if
 
 open(out_unit, file="encrypted.txt", iostat=ios)
 if (ios /= 0) then
   write(*,*) "Error opening encrypted.txt file"
   stop
 end if

! Encryption

 do 
   read(in_unit, "(a)", iostat=ios) line
   if (ios > 0) then
     write(*,*) "Error reading plain.txt file"
     stop
   else if (ios == eof) then
     exit
   end if
           
   call cipher(trim(line))
   write(out_unit, "(a)", iostat=ios) trim(line)
   if (ios /= 0) then
     write(*,*) "Error writing encrypted.txt file"
     stop
   end if
 end do
 close(in_unit)
 close(out_unit)
 open(in_unit, file="encrypted.txt",  iostat=ios)
 if (ios /= 0) then
   write(*,*) "Error opening encrypted.txt file"
   stop
 end if
 
 open(out_unit, file="decrypted.txt", iostat=ios)
 if (ios /= 0) then
   write(*,*) "Error opening decrypted.txt file"
   stop
 end if

! Decryption

 do 
   read(in_unit, "(a)", iostat=ios) line
   if (ios > 0) then
     write(*,*) "Error reading encrypted.txt file"
     stop
   else if (ios == eof) then
     exit
   end if
           
   call cipher(trim(line))
   write(out_unit, "(a)", iostat=ios) trim(line)
   if (ios /= 0) then
     write(*,*) "Error writing decrypted.txt file"
     stop
   end if
 end do  
 close(in_unit)
 close(out_unit)
 

contains

subroutine cipher(text)

 character(*), intent(in out) :: text
 integer :: i

! Substitutes A -> Z, B -> Y ... Y -> B, Z -> A and ditto for lower case ! works for both encryption and decryption

 do i = 1, len(text)
   select case(text(i:i))
     case ('A':'Z')
       text(i:i) = achar(155 - iachar(text(i:i)))
     case ('a':'z')
       text(i:i) = achar(219 - iachar(text(i:i)))
   end select
 end do

end subroutine

end program </lang>

Output:
Encrypted file:
Sviv dv szev gl wl rh gsviv droo yv z rmkfg/hlfixv urov rm dsrxs dv ziv
tlrmt gl Vmxibkg gsv urov yb ivkozxrmt vevib fkkvi/oldvi xzhv zokszyvgh
lu gsv hlfixv urov drgs zmlgsvi kivwvgvinrmvw fkkvi/oldvi xzhv zokszyvgh
li hbnyloh zmw hzev rg rmgl zmlgsvi lfgkfg/vmxibkgvw urov zmw gsvm ztzrm
xlmevig gszg lfgkfg/vmxibkgvw urov rmgl lirtrmzo/wvxibkgvw urov.
Gsrh gbkv lu Vmxibkgrlm/Wvxibkgrlm hxsvnv rh lugvm xzoovw z Hfyhgrgfgrlm Xrksvi.

Decrypted file:
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.

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

Java

<lang java>public class SubstitutionCipher {

   final static String key = "]kYV}(!7P$n5_0i R:?jOWtF/=-pe'AD&@r6%ZXs\"v*N"
           + "[#wSl9zq2^+g;LoB`aGh{3.HIu4fbK)mU8|dMET><,Qc\\C1yxJ";
   static String text = "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.";
   public static void main(String[] args) {
       String enc = encode(text);
       System.out.println("Encoded: " + enc);
       System.out.println("\nDecoded: " + decode(enc));
   }
   static String encode(String s) {
       StringBuilder sb = new StringBuilder(s.length());
       for (char c : s.toCharArray())
           sb.append(key.charAt((int) c - 32));
       return sb.toString();
   }
   static String decode(String s) {
       StringBuilder sb = new StringBuilder(s.length());
       for (char c : s.toCharArray())
           sb.append((char) (key.indexOf((int) c) + 32));
       return sb.toString();
   }

}</lang>

Encoded: "uTu]cu]b3Qu]<d]Id]...><K<,<Kd|]6KMbuTi
Decoded: Here we have to do is... Substitution Cipher.

Lua

<lang Lua>-- Generated a random substitution cipher for ASCII characters 65 to 122 function randomCipher ()

   local cipher, rnd = {plain = {}, encoded = {}}
   for ascii = 65, 122 do
       table.insert(cipher.plain, string.char(ascii))
       table.insert(cipher.encoded, string.char(ascii))
   end
   for i = 1, #cipher.encoded do
       rnd = math.random(#cipher.encoded)
       cipher.encoded[i], cipher.encoded[rnd] = cipher.encoded[rnd], cipher.encoded[i]
   end
   return cipher

end

-- Encipher text using cipher. Decipher if decode is true. function encode (text, cipher, decode)

   local output, letter, found, source, dest = ""
   if decode then
       source, dest = cipher.encoded, cipher.plain
   else
       source, dest = cipher.plain, cipher.encoded
   end
   for pos = 1, #text do
       letter = text:sub(pos, pos)
       found = false
       for k, v in pairs(source) do
           if letter == v then
               output = output .. dest[k]
               found = true
               break
           end
       end
       if not found then output = output .. letter end
   end
   return output

end

-- Main procedure math.randomseed(os.time()) local subCipher = randomCipher() print("Cipher generated:") print("\tPlain:", table.concat(subCipher.plain)) print("\tCoded:", table.concat(subCipher.encoded)) local inFile = io.open("C:\\ulua\\taskDescription.txt", "r") local input = inFile:read("*all") inFile:close() local encoded = encode(input, subCipher) print("\nEncoded file contents:") print("\t" .. encoded) print("\nAbove text deciphers to: ") print("\t" .. encode(encoded, subCipher, true))</lang>

Output:
Cipher generated:
        Plain:  ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz
        Coded:  MwZ\zPaqSTNuLQpgbnG[V_xeJmlWYCI]rhFcv^UHXijR`yfKEktADsdoBO

Encoded file contents:
        qvkv dv Hrsv Af cf Xt AHvkv dXRR hv r XyKDA/tfDkFv ^XRv Xy dHXFH dv rkv
UfXyU Af zyFkBKA AHv ^XRv hB kvKRrFXyU vsvkB DKKvk/Rfdvk Frtv rRKHrhvAt f^ AHv t
fDkFv ^XRv dXAH ryfAHvk KkvcvAvk`Xyvc DKKvk/Rfdvk Frtv rRKHrhvAt fk tB`hfRt ryc
trsv XA XyAf ryfAHvk fDAKDA/vyFkBKAvc ^XRv ryc AHvy rUrXy FfysvkA AHrA fDAKDA/vy
FkBKAvc ^XRv XyAf fkXUXyrR/cvFkBKAvc ^XRv. [HXt ABKv f^ zyFkBKAXfy/\vFkBKAXfy tF
Hv`v Xt f^Avy FrRRvc r GDhtAXADAXfy ZXKHvk.

Above text deciphers to:
        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 s
ource file with another predetermined upper/lower case alphabets or symbols and
save it into another output/encrypted file and then again convert that output/en
crypted file into original/decrypted file. This type of Encryption/Decryption sc
heme is often called a Substitution Cipher.

Perl 6

Works with: Rakudo version 2015-11-20

Feed it an action (encode, decode) and a file name at the command line and it will spit out the (en|de)coded file to STDOUT. Redirect into a file to save it. If no parameters are passed in, does the demo encode/decode. <lang perl6>my $chr = (' ' .. '}').join(); my $key = $chr.comb.pick(*).join();

  1. Be very boring and use the same key every time to fit task reqs.

$key = q☃3#}^",dLs*>tPMcZR!fmC rEKhlw1v4AOgj7Q]YI+|pDB82a&XFV9yzuH<WT%N;iS.0e:`G\n['6@_{bk)=-5qx(/?$JoU☃;

sub MAIN ($action = 'encode', $file = ) {

   die 'Only options are encode or decode.' unless $action ~~ any 'encode'|'decode';
   my $text = qq:to/END/;
       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.
       END
   $text = $file.IO.slurp if $file;
   say "Key = $key\n";
   if $file {
       say &::($action)($text);
   } else {
       my $encoded;
       say "Encoded text: \n {$encoded = encode $text}";
       say "Decoded text: \n {decode $encoded}";
   }

}

sub encode ($text) { $text.trans($chr => $key) }

sub decode ($text) { $text.trans($key => $chr) }</lang>

Output:

with no passed parameters

Key = 3#}^",dLs*>tPMcZR!fmC rEKhlw1v4AOgj7Q]YI+|pDB82a&XFV9yzuH<WT%N;iS.0e:`G\n['6@_{bk)=-5qx(/?$JoU

Encoded text: 
 +`=`3(`3n.x`35b3:b3[-35n`=`3([@@30`3.3[{kq5Z-bq=e`3G[@`3[{3(n[en3
(`3.=`3\b[{\35b3]{e=?k535n`3G[@`30?3=`k@.e[{\3`x`=?3qkk`=Z@b(`=3
e.-`3.@kn.0`5-3bG35n`3-bq=e`3G[@`3([5n3.{b5n`=3k=`:`5`=_[{`:3
qkk`=Z@b(`=3e.-`3.@kn.0`5-3b=3-?_0b@-3.{:3-.x`3[53[{5b3.{b5n`=3
bq5kq5Z`{e=?k5`:3G[@`3.{:35n`{3.\.[{3eb{x`=535n.53bq5kq5Z`{e=?k5`:3
G[@`3[{5b3b=[\[{.@Z:`e=?k5`:3G[@`c39n[-35?k`3bG3]{e=?k5[b{ZQ`e=?k5[b{
-en`_`3[-3bG5`{3e.@@`:3.3Vq0-5[5q5[b{37[kn`=c

Decoded text: 
 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.

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 & demonstrates a substitution cipher for the 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 using ERASE. */

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

zkl

<lang zkl>class SubstitutionCipher{

  // 92 characters: " !"#$%&" ... "xyz{|}", doesn't include "~"
  const KEY="]kYV}(!7P$n5_0i R:?jOWtF/=-pe'AD&@r6%ZXs\"v*N"
            "[#wSl9zq2^+g;LoB`aGh{3.HIu4fbK)mU8|dMET><,Qc\\C1yxJ";
  fcn encode(s){ s.apply(fcn(c){ try{ KEY[c.toAsc()-32] }catch{ c } }) }
  fcn decode(s){ s.apply(fcn(c){ try{ (KEY.index(c)+32).toChar() }catch{ c } }) }

}</lang> <lang zkl>text:="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.";

encoded:=SubstitutionCipher.encode(text); println( "Encoded: ",encoded); println("\nDecoded: ",SubstitutionCipher.decode(encoded));</lang>

Output:
Encoded: "uTu]cu]b3Qu]<d]Id]K>]<buTu]cKUU].u]3]K|M,< >d,THu]4KUu]K|]cbKHb]cu]3Tu]fdK|f]<d]Z|HTCM<]<bu]4KUu].C]TuMU3HK|f]uQuTC],MMuT UdcuT]H3>u]3UMb3.u<>]d4]<bu]>d,THu]4KUu]cK<b]3|d<buT]MTuIu<uT8K|uI],MMuT UdcuT]H3>u]3UMb3.u<>]dT]>C8.dU>]3|I]>3Qu]K<]K|<d]3|d<buT]d,<M,< u|HTCM<uI]4KUu]3|I]<bu|]3f3K|]Hd|QuT<]<b3<]d,<M,< u|HTCM<uI]4KUu]K|<d]dTKfK|3U IuHTCM<uI]4KUui]2bK>]<CMu]d4]Z|HTCM<Kd| %uHTCM<Kd|]>Hbu8u]K>]d4<u|]H3UUuI]3]q,.><K<,<Kd|]6KMbuTi

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