Substitution cipher: Difference between revisions
m (→{{header|REXX}}: added/changed whitespace and comments.) |
|||
Line 431: | Line 431: | ||
::* the two records should be equal in the number of characters. |
::* the two records should be equal in the number of characters. |
||
::* the N<sup>th</sup> character of record '''1''' will be encrypted to the N<sup>th</sup> character of record '''2'''. |
::* the N<sup>th</sup> character of record '''1''' will be encrypted to the N<sup>th</sup> character of record '''2'''. |
||
<lang rexx>/*REXX program implements a |
<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*/ |
parse arg fid.1 fid.2 fid.3 fid.4 . /*obtain optional arguments from the CL*/ |
||
if fid.1=='' then fid.1= |
if fid.1=='' then fid.1= "CIPHER.IN" /*Not specified? Then use the default.*/ |
||
if fid.2=='' then fid.2= |
if fid.2=='' then fid.2= "CIPHER.OUT" /* " " " " " " */ |
||
if fid.3=='' then fid.3= |
if fid.3=='' then fid.3= "CIPHER.KEY" /* " " " " " " */ |
||
if fid.4=='' then fid.4= |
if fid.4=='' then fid.4= "CIPHER.ORI" /* " " " " " " */ |
||
say ' input file: ' fid.1 /*display the fileID used for input. */ |
say ' input file: ' fid.1 /*display the fileID used for input. */ |
||
say ' output file: ' fid.2 /* " " " " " output. */ |
say ' output file: ' fid.2 /* " " " " " output. */ |
||
say ' cipher file: ' fid.3 /* " " " " " cipher-key*/ |
say ' cipher file: ' fid.3 /* " " " " " cipher-key*/ |
||
say 'decrypted file: ' fid.4 /* " " " " " decrypted*/ |
say 'decrypted file: ' fid.4 /* " " " " " decrypted*/ |
||
call closer /*close all files in case they're open.*/ |
call closer /*close all files in case they're open.*/ |
||
say |
say |
||
do c=1 while lines(fid.3)\==0 /*read (hopefully 2 records) from key. */ |
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.*/ |
@.c=space(linein(fid.3),0) /*assign input record to an @. array.*/ |
||
end /*c*/ |
end /*c*/ |
||
c=c-1 /*adjust the number of recores (for DO)*/ |
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==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 many records (>2).' |
||
if c<2 then call ser fid.3, 'has too few 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.' |
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.*/ |
call encrypt fid.1, fid.2 /*encrypt the input file ───► output.*/ |
||
_=@.1; @.1=@.2; @.2=_ /*switch the cipher keys for decryption*/ |
_=@.1; @.1=@.2; @.2=_ /*switch the cipher keys for decryption*/ |
||
call encrypt fid.2, fid.4 /*decrypt the output file ───► decrypt.*/ |
call encrypt fid.2, fid.4 /*decrypt the output file ───► decrypt.*/ |
||
call show 'cipher file ('fid.3")" , fid.3 /*display the |
call show 'cipher file ('fid.3")" , fid.3 /*display the cipher─key file. */ |
||
call show 'input file ('fid.1")" , fid.1 /* " " input " */ |
call show 'input file ('fid.1")" , fid.1 /* " " input " */ |
||
call show 'output file ('fid.2")" , fid.2 /* " " output " */ |
call show 'output file ('fid.2")" , fid.2 /* " " output " */ |
||
call show ' decrypted file ('fid.4")" , fid.4 /* " " decrypted " */ |
call show ' decrypted file ('fid.4")" , fid.4 /* " " decrypted " */ |
||
exit /*stick a fork in it, we're all done. */ |
exit /*stick a fork in it, we're all done. */ |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
/*────────────────────────────────────────────────────────────────────────────*/ |
|||
closer: |
closer: do f=1 for 4; call lineout fid.f; end /*f*/; return |
||
ser: say '***error!*** file ' arg(1)" " arg(2); |
ser: say '***error!*** file ' arg(1)" " arg(2); exit |
||
show: say; say center(arg(1),79,'═'); |
show: say; say center(arg(1),79,'═'); "TYPE" arg(2); return |
||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
/*────────────────────────────────────────────────────────────────────────────*/ |
|||
encrypt: parse arg @in,@out; |
encrypt: parse arg @in,@out; 'ERASE' @out /*delete the output file using ERASE. */ |
||
do j=1 while lines(@in)\==0 |
do j=1 while lines(@in)\==0 |
||
call lineout @out, translate(linein(@in), @.2, @.1) |
call lineout @out, translate(linein(@in), @.2, @.1) |
||
end /*j*/ |
end /*j*/ |
||
j=j-1 /*adjust the number of recores (for DO)*/ |
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? */ |
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.*/ |
say @in ' records processed: ' j /*show the number of records processed.*/ |
||
call closer /*close all the files to be neat & safe*/ |
call closer /*close all the files to be neat & safe*/ |
||
return</lang> |
return</lang> |
||
'''output''' when using the default input files: |
'''output''' when using the default input files: |
||
<pre> |
<pre> |
Revision as of 09:06, 13 March 2016
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++
The key file should look something like this: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789SWoVR0kJLXQ8zbCd1OagTH5ie3nvYU2wfrM9yI4sKm6c7hNjtADqFPxpEZlBuG <lang cpp>
- include <iostream>
- include <string>
- 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>
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...
Perl 6
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();
- 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 ----