Search the Catalog
JavaScript Application Cookbook

JavaScript Application Cookbook

By Jerry Bradenbaugh
1st Edition September 1999
1-56592-577-7, Order Number: 5777
478 pages, $34.95

Chapter 9
Ciphers in JavaScript

Application Features

  • Message Encryption Application Using Multiple Ciphers
  • Object-Oriented Design Makes Adding Ciphers Easy
  • Entertaining Application and Utility for Your Visitors

JavaScript Techniques

  • Assigning Methods to Your Objects
  • More String Matching and Replacing
  • Tapping into JavaScript Object Inheritance
  • Using Alternate Syntax

If you just finished the previous chapter, this one will give your brain a little breather. This chapter is lighter and deals with an application based on pure, simple fun--ciphering techniques with JavaScript. The application jumbles text messages into what seems like a bunch of junk, meaningful only to those who possess the key to reveal its secret.

This interface displayed in Figure 9-1 is fairly simple. With the Caesar cipher selected, it's just a paragraph describing the cipher, a select list used to choose a number key, and a text area to enter text to encipher and decipher.

Figure 9-1. The cipher interface

 

The text "JavaScript is the scripting language of choice across the planet, don't you agree?" is entered in the text area. Selecting 6 from the Shift list, then choosing the "Encipher" button yields the scrambled text you see in Figure 9-2. Here it is again:

pg1gyixovz oy znk yixovzotm rgtm0gmk ul inuoik jutz 4u0 gmxkk
Figure 9-2. Using the Caesar cipher

 

Choosing "Decipher" returns the text to its original form. Notice that the text is returned in all lowercase. The Vigenere cipher works about the same way. Choose "Vigenere Cipher" from the cipher list at the top, and Figure 9-3 is what you'll see.

Figure 9-3. The Vigenere cipher interface

 

With this cipher, there is no select list to choose a number key. This time, there is a field to enter a word or phrase as the key. Check out how the term "code junky" ciphers the original text. It's listed here and also shown in Figure 9-4:

loye1w4sdv lw duo uqumydvx4 zdrpenq2 2i l11s0g dg0852 vvh y5nx2v gswd 8cw dk0yr
Figure 9-4. The Vigenere cipher in action

 

Of course, choosing "Decipher" using "code junky" as the key returns the text to meaningful form. Since you might be new to the concept of ciphers, here's a crash course on the subject. It explains cipher basics and offers details about the two ciphers used in the application--the Caesar cipher and the Vigenére cipher.

How Ciphers Work

So, what is a cipher anyway? A cipher is an algorithm or set of algorithms that systematically convert a sender's intended message text to what appears to be meaningless text, which can be converted back to the sender's original message only by authorized recipients. The following terms and definitions will help you understand ciphering and deciphering in general and the code behind them.

The term plaintext refers the sender's original message. The meaning in plaintext is what the sender wants to convey to the recipient(s).

The term ciphertext refers to plaintext whose appearance has been encrypted, or algorithmically changed. Ciphertext becomes plaintext once it has been decrypted.

Many ciphers use one or more keys. A key is string of text or bits used to encrypt or decrypt data. RSA Data Security, Inc. (http://www.rsa.com/ ), a leading encryption technology firm, states that a key determines the mapping of the plaintext to the ciphertext. A key could be just about anything, such as the word "cleveland," the phrase "winners never quit, quitters never win," the binary number 10011011, or even some wild string, such as %_-.;,(<<*&^.

Ciphers in which both the sender and the recipient use the same key to encrypt and decrypt the message are said to be part of a symmetric-key cryptosystem. Ciphers in which data is encrypted and decrypted with a pair of keys--one freely distributed to the public, the other known only to the recipient--are said to be part of a public-key cryptosystem. Ciphers in this application employ an asymmetric-key cryptosystem.

There are hundreds of documented ciphers. Some date back thousands of years, devised by great leaders or scientists of the past; others date back to only last week, devised by some geeky teenager who experienced epiphany after setting a personal high score on Tomb Raider. Whatever the source, ciphers fall into three general categories: concealment, transposition, and substitution.

Concealment ciphers include the plaintext within the ciphertext. It is up to the recipient to know which letters or symbols to exclude from the ciphertext in order to yield the plaintext. Here is an example of a concealment cipher:

i2l32i5321k34e1245ch456oc12ol234at567e

Remove all the numbers, and you'll have i like chocolate. How about this one?

Larry even appears very excited. No one worries.

The first letter from each word reveals the message leave now. Both are easy, indeed, but many people have crafted more ingenious ways of concealing the messages. By the way, this type of cipher doesn't even need ciphertext, such as that in the above examples. Consider the invisible drying ink that kids use to send secret messages. In a more extreme example, a man named Histiaeus, during 5th century B.C., shaved the head of a trusted slave, then tattooed the message onto his bald head. When the slave's hair grew back, Histiaeus sent the slave to the message's intended recipient, Aristagoros, who shaved the slave's head and read the message instructing him to revolt.

Transposition ciphers also retain the characters of the plaintext within the ciphertext. Ciphertext is created simply by changing the order of the existing plaintext characters. Try this one:

uo yn os dn ep ed yx al ag eh tf oy te fa se ht

Bunch those letters together, then reverse their order. You'll get the message "the safety of the galaxy depends on you."

Substitution ciphers replace each character of plaintext with another character or symbol. Consider this:

9-15-14-12-25-20-8-9-14-11-9-14-14-21-13-2-5-18-19

If you substitute each number with the associated letter of the alphabet, you'll reveal the phrase "I only think in numbers." (For example, "I" is the 9th letter of the alphabet, "o" is the 15th, etc.) Substitution ciphers can utilize just about any character set for encryption and decryption. Both ciphers in this application are substitution ciphers.

A Few Words on Cracking the Code

The ciphertext that this application generates can, at first glance, look remarkably complex. In reality, any decent cryptanalyst could break the cipher in a matter of minutes with only a pencil and paper. Fortunately, security is much more ensured by using such algorithms as the RSA, IDEA, and triple DES. I can't show you how to crack those, but I'll give you a hint about why simple substitution and transposition ciphers are so vulnerable.

The primary weapon against these types of ciphers is letter-frequency distribution. That is, some letters show up more than others in everyday conversation in the English language. The most common letters in the English language, from most to least frequent, are E-T-N-R-O-A-I-S. The least common are J, K, Q, X, and Z.[1]

Another way to compromise a simple cipher is to analyze digraphs and trigraphs. A digraph is a two-character string, such as ab or cd. A trigraph is a three-letter string, such as abc or bcd. Digraphs and trigraphs also have high and low frequencies in the English language. The U.S. Army considers the following digraphs most frequent: en, er, re, nt, th, on, and in. The least frequent are df, hu, ia, lt, and mp. For trigraphs, the most common are ent, ion, and, ing, ive, tho, and for. The least common are eri, hir, iet, der, and dre.

The most frequent letters, digraphs, and trigraphs not only hint at what many letters might be, but also indicate what they and surrounding letters probably are not. Consider how many digraphs and trigraphs you use in everyday conversation: is, be, am, or, not, are, yes, the. The list goes on. Even though the ciphers used in this application aren't top quality, they're still a lot of fun, and a great way to keep out the casual nosey intruder.

The Caesar Cipher

Used by Julius Caesar to communicate with his army general, this cipher is one of the first known to be used for securing messages. The algorithm here is simply to shift the letters of the alphabet between 1 and 25 places (from b-z) so that a shift of 3 causes a plaintext letter a to become a ciphertext d, and vice versa. Letters that are shifted past z resume at the beginning. In other words, a shift of 3 converts a plaintext z to a ciphertext c. The number is the key that both sending and receiving parties use to encipher and decipher the message.

Notice that once a key is chosen, each character always has the same corresponding plaintext or ciphertext character associated with it. For example, a shift of 3 means that the plaintext a is always a ciphertext d. That is, there is only one cipher alphabet. The Caesar cipher is said to be monoalphabetic.

The Vigenere Cipher

This cipher was proposed by mathematician Blaise de Vigenere in the 16th century. It is a polyalphabetic cipher because it uses more than one cipher alphabet. In other words, a plaintext a does not always equal a ciphertext d, as the Caesar cipher does with a shift of 3.

Instead of a number, this cipher utilizes a keyword. Suppose you want to cipher the plaintext meet at midnight, and you choose the keyword vinegar. The letters of the keyword are then lined up in succession with the letters of the plaintext, like so:

vine ga rvinegar
meet at midnight

OK. V is the 22nd letter in the alphabet. I is 9th. Letters n, e, g, a, and r are 14th, 5th, 7th, 1st, and 18th, respectively. So plaintext letter m is shifted 22, the first e is shifted 9, the second e is shifted 14, and so on. Here's what you get:

hmrx gt ddlammhk

If you think about it, this cipher is like the Caesar cipher on the fly. A new Caesar is performed on every character.

TIP: If you want to learn more about ciphers, you can download a multitude of the once "classified" U.S. Army documents in PDF format at http://www.und.nodak.edu/org/crypto/crypto/army.field.manual/separate.chaps/.

This copy is stored on the web site of the Crypto Drop Box. Check out the home page at http://www.und.nodak.edu/org/crypto/crypto/. You'll find enough resources there to keep you busy for days.

Execution Requirements

This application uses JavaScript 1.2 and DHTML only, so browsers 4.x and higher are allowed to play. There is a lot of string matching and replacement, which makes JavaScript 1.2 really shine.

The Syntax Breakdown

Fortunately, this application requires only two files. Better yet, we'll only be looking at the code in one of them. The two files are index.html and dhtml.js (dhtml.js is covered in Chapter 6, Implementing JavaScript Source Files). Before we look at any code, let's consider a few abstract concepts about how this application might "look." This application is constructed from a very basic object-oriented perspective. The shopping cart in Chapter 8, Shopping Bag: The JavaScript Shopping Cart, covers another application that utilizes object orientation, but the cipher app takes that approach a little further.

There are two ciphers in this application. Each cipher has certain things in common with all other ciphers, no matter what kind of cipher each may be. Remember that there are three basic types of ciphers--concealment, transposition, and substitution. This application contains two substitution ciphers: the Caesar cipher and the Vigenére cipher. Figure 9-5 shows a basic structure of the hierarchy just described.

Figure 9-5. The cipher structure

 

Figure 9-6. Extending the cipher structure

 

The figure shows that the ConcealmentCipher, TranspositionCipher, and SubstitutionCipher objects inherit everything from the Cipher object, somewhat like a subclass. Therefore, the Vigenére cipher and the Caesar cipher are instances of the SubstitutionCipher object and contain all its properties and methods.

For the sake of intellectual curiosity, let's see how this model can be extended. Figure 9-6 shows how other cipher types can easily be added to the hierarchy without redesigning anything. The bold portion of the structure identifies the part of the hierarchy used in the application.

As you can see, the number of cipher types and individual ciphers can be added to this structure, ad infinitum, without changing any of the existing code of the ciphers currently in place. You can also "subclass" the subclasses. Object-oriented design proves beneficial once again. Keep this in mind as we go through the supporting code in the next few pages. You'll see how easy it is to add more ciphers to the application without having to retool. The "Potential Extensions" section offers a few parting words on more ciphers to add.

Let's take a look at index.html in Example 9-1.

Example 9-1: index.html

  1. <HTML>
  2. <HEAD>
  3. <TITLE>Cipher</TITLE>
  4. <STYLE TYPE="text/css">
  5. <!--
  6. BODY { margin-left: 50 px; font-family: arial; }
  7. I { font-weight: bold; }
  8. //-->
  9. </STYLE>
  10. <SCRIPT LANGUAGE="JavaScript1.2" SRC="dhtml.js"></SCRIPT>
  11. <SCRIPT LANGUAGE="JavaScript1.2">
  12. <!--
  13.  
  14. var caesar = '<FONT SIZE=2>Made famous by Julius Caesar, this cipher ' +
  15. 'performs character shifting (substitution). Plaintext is ' +
  16. 'enciphered by shifting forward each character of the alphabet a ' +
  17. 'fixed number of characters.<BR><BR>For example, shifting by 1 ' +
  18. 'changes plaintext <I>a</I> to <I>b</I>, <I>b</I> to <I>c</I>, ' +
  19. 'and so on. Plaintext characters at the end of, say, the alphabet, ' +
  20. 'are enciphered by starting at the beginning. In other words, ' +
  21. '<I>z</I> becomes <I>a</I>. This application also includes digits ' +
  22. '0-9. So a plaintext <I>z</I> becomes <I>0</I>, and a plaintext ' +
  23. '<I>9</I> becomes <I>a</I>. The process is reversed for deciphering.' +
  24. '<BR><FORM>Shift: ' +
  25. genSelect('Shift', 35, 0, 0) +
  26. '</FORM><BR>Note: Caesar was rumored to prefer a shift of 3.';
  27.  
  28. var vigenere = '<FONT SIZE=2>Made famous by mathematician Blaise de ' +
  29. 'Vigenere, the Vigenere cipher can be considered a "dynamic" ' +
  30. 'version of the Caesar cipher. Instead of shifting each plaintext ' +
  31. 'character by a fixed number, this cipher shifts characters ' +
  32. 'according to the character index of a keyword you choose such as ' +
  33. '<I>dog</I>.<BR><BR>Since <I>d</I>, <I>o</I>, and <I>g</I> are ' +
  34. 'letters 4, 15, and 7 of the alphabet, each three plaintext ' +
  35. 'characters are shifted by 4, 15, and 7, respectively. This ' +
  36. 'application includes digits 0-9. So your keyword can have letters ' +
  37. 'and numbers.<BR><BR><FORM>Keyword: <INPUT TYPE=TEXT NAME="KeyWord" ' +
  38. 'SIZE=25></FORM><BR>Note: This cipher has many versions, one of ' +
  39. 'which was devised by Lewis Carroll, author of Alice in Wonderland.';
  40.  
  41. var curCipher = "caesar";
  42.  
  43. function Cipher() {
  44. this.purify = purify;
  45. this.chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
  46. }
  47.  
  48. function purify(rawText) {
  49. if (!rawText) { return false; }
  50. var cleanText = rawText.toLowerCase();
  51. cleanText = cleanText.replace(/\s+/g,' ');
  52. cleanText = cleanText.replace(/[^a-z0-9\s]/g,'');
  53. if(cleanText.length == 0 || cleanText.match(/^\s+$/) != null) {
  54. return false;
  55. }
  56. return cleanText
  57. }
  58.  
  59. function SubstitutionCipher(name, description, algorithm) {
  60. this.name = name;
  61. this.description = description;
  62. this.substitute = substitute;
  63. this.algorithm = algorithm;
  64. }
  65. SubstitutionCipher.prototype = new Cipher;
  66.  
  67. function substitute(baseChar, shiftIdx, action) {
  68. if (baseChar == ' ') { return baseChar; }
  69. if(action) {
  70. var shiftSum = shiftIdx + this.chars.indexOf(baseChar);
  71. return (this.chars.charAt((shiftSum < this.chars.length) ?
  72. shiftSum : (shiftSum % this.chars.length)));
  73. }
  74. else {
  75. var shiftDiff = this.chars.indexOf(baseChar) - shiftIdx;
  76. return (this.chars.charAt((shiftDiff < 0) ?
  77. shiftDiff + this.chars.length : shiftDiff));
  78. }
  79. }
  80.  
  81. function caesarAlgorithm (data, action) {
  82. data = this.purify(data);
  83. if(!data) {
  84. alert('No valid text to ' + (action ? 'cipher.' : 'decipher.'));
  85. return false;
  86. }
  87. var shiftIdx =
  88. (NN ? refSlide("caesar").document.forms[0].Shift.selectedIndex : document.forms[1].Shift.selectedIndex);
  89. var cipherData = '';
  90. for (var i = 0; i < data.length; i++) {
  91. cipherData += this.substitute(data.charAt(i), shiftIdx, action);
  92. }
  93. return cipherData;
  94. }
  95.  
  96. function vigenereAlgorithm (data, action) {
  97. data = this.purify(data);
  98. if(!data) {
  99. alert('No valid text to ' + (action ? 'cipher.' : 'decipher.'));
  100. return false;
  101. }
  102. var keyword = this.purify((NN ?
  103. refSlide("vigenere").document.forms[0].KeyWord.value :
  104. document.forms[2].KeyWord.value));
  105. if(!keyword || keyword.match(/\^s+$/) != null) {
  106. alert('No valid keyword for ' + (action ? 'ciphering.' :
  107. 'deciphering.'));
  108. return false;
  109. }
  110. keyword = keyword.replace(/\s+/g, '');
  111. var keywordIdx = 0;
  112. var cipherData = '';
  113. for (var i = 0; i < data.length; i++) {
  114. shiftIdx = this.chars.indexOf(keyword.charAt(keywordIdx));
  115. cipherData += this.substitute(data.charAt(i), shiftIdx, action);
  116. keywordIdx = (keywordIdx == keyword.length - 1 ? 0 : keywordIdx + 1);
  117. }
  118. return cipherData;
  119. }
  120.  
  121. var cipherArray = [
  122. new SubstitutionCipher("caesar", caesar, caesarAlgorithm),
  123. new SubstitutionCipher("vigenere", vigenere, vigenereAlgorithm)
  124. ];
  125.  
  126. function showCipher(name) {
  127. hideSlide(curCipher);
  128. showSlide(name);
  129. curCipher = name;
  130. }
  131.  
  132. function routeCipher(cipherIdx, data, action) {
  133. var response = cipherArray[cipherIdx].algorithm(data, action);
  134. if(response) {
  135. document.forms[0].Data.value = response;
  136. }
  137. }
  138.  
  139. //-->
  140. </SCRIPT>
  141. </HEAD>
  142. <BODY BGCOLOR=#FFFFFF>
  143.  
  144. <DIV>
  145. <TABLE BORDER=0>
  146. <TR>
  147. <TD ALIGN=CENTER COLSPAN=3>
  148. <IMG SRC="images/cipher.jpg">
  149. </TD>
  150. </TR>
  151. <TR>
  152. <TD VALIGN=TOP WIDTH=350>
  153. <FORM>
  154. <SELECT NAME="Ciphers"
  155. onChange="showCipher(this.options[this.selectedIndex].value);">
  156. <OPTION VALUE="caesar">Caesar Cipher
  157. <OPTION VALUE="vigenere">Vigenére Cipher
  158. </SELECT>
  159. </TD>
  160. <TD ALIGN=CENTER>
  161. <TEXTAREA NAME="Data" ROWS="15" COLS="40" WRAP="PHYSICAL"></TEXTAREA>
  162. <BR><BR>
  163. <INPUT TYPE=BUTTON VALUE="Encipher"
  164. onClick="routeCipher(this.form.Ciphers.selectedIndex,
  165. this.form.Data.value, true);">
  166. <INPUT TYPE=BUTTON VALUE="Decipher"
  167. onClick="routeCipher(this.form.Ciphers.selectedIndex,
  168. this.form.Data.value, false);">
  169. <INPUT TYPE=BUTTON VALUE=" Reset "
  170. onClick="this.form.Data.value='';">
  171. </FORM>
  172. </TD>
  173. </TR>
  174. </TABLE>
  175. </DIV>
  176.  
  177. <SCRIPT LANGUAGE="JavaScript1.2">
  178. <!--
  179. document.forms[0].Ciphers.selectedIndex = 0;
  180. genLayer("caesar", 50, 125, 350, 200, showName, caesar);
  181. genLayer("vigenere", 50, 125, 350, 200, hideName, vigenere);
  182. //-->
  183. </SCRIPT>
  184. </BODY>
  185. </HTML>

The JavaScript source file dhtml.js is the first code interpreted. The code in that file utilizes DHTML to set up the layers and generate select lists on the fly. We'll get to that shortly. The next code of interest comes in lines 14-39. Variables caesar and vigenere are designed and set to the value of HTML strings. Each of these, as you might have guessed, defines an interface layer of a cipher. Everything is static, expect for the call to function genSelect() in the value of caesar. Here it is:

genSelect('Shift', 35, 0, 0)

This creates a select list named Shift, which starts at 0, ends at 35, and has Option 0 selected. The VALUE and TEXT attributes are set to the number used for counting. This code comes straight from Chapter 5, ImageMachine. If you made the JavaScript library that I had suggested in Chapter 6, you should definitely have included this code in it. You'll find this function defined at the bottom of dhtml.js.

Defining a Cipher

The next lines of code define all ciphers that are and will be. Lines 43-46 contain the Cipher() constructor:

function Cipher() {
  this.purify = purify;
  this.chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
  }

That's a pretty small constructor. If you expected some huge complex definition with all sorts of differential equations and spherical geometry designed to split the fourth dimension, sorry to let you down. Cipher() defines ciphers at a really high level. The only two assumptions made about all ciphers in this application is that:

JavaScript Technique:
Assigning Methods to Your Objects

As small as it is, the Cipher() constructor introduces a new concept. That is, objects we've created in other chapters contained only properties. The Cipher() constructor has a property called chars, but also has a method named purify().

Properties are easy to assign. Just assign the value you want to a variable using the this.variable_name syntax. Assigning methods is a little different. You first define a function, then use the same this.variable_name syntax to refer to the function. That's exactly what happens in the Cipher() constructor. The script has function purify() defined. Cipher() has a variable named this.purify referencing the purify method. Notice that are no parentheses. This identifies a reference. Had this.purify been set to purify(), the purify() function would have been called, and this.purify would be set to whatever the function returned.

Referring to a function within a constructor assigns a purify() method to any variable set to new Cipher(). That's what happens with the elements in cipherArray, as you'll soon see.

No matter if the data will be enciphered or deciphered, it must conform to certain rules. Here are those rules:

Nice rules. Simple, too. All we need is something to enforce them. Enter function purify() in lines 48-57:

function purify(rawText) {
  if (!rawText) { return false; }
  var cleanText = rawText.toLowerCase();
  cleanText = cleanText.replace(/\s+/g,' ');
  cleanText = cleanText.replace(/[^a-z0-9\s]/g,'');
  if(cleanText.length == 0 || cleanText.match(/^\s+$/) != null) { 
    return false; 
    }
  return cleanText
  }

This function returns one of two values: false or formatted text ready for cipher action. Returning false will cancel any cipher operation. If rawText contains anything to format, purify() first converts all letters to lowercase. Here's how:

cleanText = cleanText.replace(/\s+/g,' ');

Using regular expression matching, the replace() method of the String object searches for all the whitespaces in the entire string, which are replaced by a single whitespace, no matter how many adjacent ones there are. After that, purify() replaces all other characters that are not a-z or 0-9 or single whitespaces with an empty character. This removes all non-qualifying characters. Here is the workhorse replace() method at work again:

cleanText = cleanText.replace(/[^a-z0-9\s]/g,'');

JavaScript Technique:
More String Matching and Replacing

You just have to love JavaScript 1.2's regular expression matching. This application makes more use of it than previous applications. Let's have another look at the regular expression in line 52.

/[^a-z0-9\s]/g

Although it isn't long, the syntax might be a little confusing. This regular expression is known as a negated character set. In other words, anything not contained within the definition constitutes a match. You can utilize square brackets in a regular expression to specify a range of characters to include (or in this case, exclude). Consider this:

/[a-z]/g

This expression matches any of the lowercase letters of the alphabet. The g indicates that the search matches all characters in this range, not just the first one encountered You can include as many ranges as you like.

/[a-z0-9\s]/g

This expression matches any of the lowercase letters of the alphabet or any digit or whitespace. However, the cipher application in this case is interested in anything that does not match these. The circumflex (^) inside square brackets negates any special characters after it, which yields our original syntax.

/[^a-z0-9\s]/g

This is the tip of the string-matching iceberg. You can use these regular expressions to validate and format social security numbers, email addresses, URLs, phone numbers, zip codes, dates, times, and more. If you're new to regular expressions, you can get the full reference of regular expression definitions and special character meanings at "What's New In JavaScript 1.2" at http://developer1.netscape.com:80/docs/manuals/communicator/jsguide/regexp.htm.

The formatting is now complete. The time has come to check whether there is anything useful remaining to cipher. As long as the formatted string contains at least one character that is a-z or 0-9, everything is fine. However, there are two cases where this is untrue:

If either is the case, it's time to call off the operation and hold out for better data. Lines 53-55 perform the check. This causes purify() to return false if either occurs:

if(cleanText.length == 0 || cleanText.match(/^\s+$/) != null) { 
  return false; 
  }

As far as knowing which characters qualify, Cipher uses the following string:

this.chars = 'abcdefghijklmnopqrstuvwxyz0123456789';

Defining a Substitution Cipher

Now that the mother of all cipher objects--Cipher()--has been defined, let's create a more specific version. That's right--the spec for all substitution ciphers: SubstitutionCipher(). Study lines 59-65:

function SubstitutionCipher(name, description, algorithm) {
  this.name = name;
  this.description = description;
  this.substitute = substitute;
  this.algorithm = algorithm;
  }
SubstitutionCipher.prototype = new Cipher;

The assumption for every cipher object is that each one knows how to format any user data. Substitution ciphers contain further assumptions. Here they are:

  1. Each has a name and description.
  2. Each uses a general method for substituting characters for both enciphering and deciphering.
  3. Each has a specific implementation of the general substitution method. This is what makes one substitution cipher different from other substitution ciphers.
  4. Each SubstitutionCipher object is also a cipher object.

Assigning a name and description to each is pretty simple. Any two strings you pass in when you call new SubstitutionCipher() will work just fine. Incidentally, the variables caesar and vigenere instantiated earlier with all that HTML will be the description of each. That takes care of the first assumption. Now, what about defining a general substitution method? This method can substitute one character for another. That's it. Each call to this method returns one character, which is a substitute for another.

Performing Basic Substitution

Each SubstitutionCipher uses the same method to replace one character in the chars string with another. The substitute() function, shown below, is defined as a method for each instantiation of SubstitutionCipher:

function substitute(baseChar, shiftIdx, action) {
  if (baseChar == ' ') { return baseChar; }
  if(action) {
    var shiftSum = shiftIdx + this.chars.indexOf(baseChar);
    return (this.chars.charAt((shiftSum < this.chars.length) ? 
      shiftSum : (shiftSum % this.chars.length)));
    }
	else {
    var shiftDiff = this.chars.indexOf(baseChar) - shiftIdx;
    return (this.chars.charAt((shiftDiff < 0) ? 
      shiftDiff + this.chars.length : shiftDiff));
    }
  }

This method expects three arguments. baseChar is the character that will be replaced by another. shiftIdx is an integer that determines "how much" shift to apply in order to find the correct substitution. action is a Boolean value that specifies whether baseChar should be treated as plaintext or ciphertext. To leave whitespace unchanged, the first line returns baseChar as is if baseChar is indeed a whitespace. Otherwise, this method uses action to determine how to calculate the amount of shift. If action is true, the enciphering algorithm is used. If action is false, the deciphering algorithm is used.

Remember that chars contains a string of all the qualifying characters. The enciphering algorithm simply determines the index of baseChar within chars, then chooses the character of chars at that index plus the value of shiftIdx.

Here's an example. Suppose that baseChar is d, shiftIdx is 8, and chars.indexOf(`d') is 3. That brings us to line 70:

var shiftSum = shiftIdx + this.chars.indexOf(baseChar);

Variable shiftSum equals 11 (8 + 3). So chars.charAt(11) is the letter l. That is what substitute() would return in this case. That seems straightforward. It is, but suppose baseChar is letter o, and shiftIdx is 30. Check the math. shiftSum now equals 45. The problem is, chars has only 36 characters (a-z and 0-9). Therefore, chars.charAt(45) doesn't exist.

When the algorithm reaches the last character of chars, it must "wrap" around and start over with 0, and begin adding again from there. You can use the modulus operator to get the desired effect. Think about it: the modulus operator returns the integer remainder of two operands. Here are several examples:

4 % 3 = 1. Dividing 4 by 3 leaves a remainder of 1.
5 % 3 = 2. Dividing 5 by 3 leaves a remainder of 2.
6 % 3 = 0. Dividing 6 by 3 leaves no remainder.

All you need to do is use the return of the modulus operation. So instead of using a shiftSum of 45, you would use shiftSum % chars.length, which equals 6. chars.charAt(6) is the letter g. This explains the ensuing code for the enciphering algorithm:

return (this.chars.charAt((shiftSum < this.chars.length) ? shiftSum : 
  (shiftSum % this.chars.length)));

In this case, substitute() returns chars.charAt(shiftSum) or chars.charAt(shiftSum % this.chars.length), depending on the size of shiftSum and the length of chars. How about the keyword this? You may be wondering what it is doing there. Keep in mind that substitute() is not a function; it is a method of whatever variable is instantiated as a SubstitutionCipher. Using this, within this method will refer to any property of the instantiated variable. Since SubstitutionCipher inherits all the properties of Cipher, the instantiated variable "owns" a property called chars.

The procedure isn't much different for the deciphering algorithm. The only change is that it subtracts shiftIdx to reach the correct character in chars. In this case, variable shiftDiff is set to the difference of the index of baseChar and shiftIdx, which is as follows.

var shiftDiff = this.chars.indexOf(baseChar) - shiftIdx;

Again, this is fairly simple. If shiftDiff is less than 0, however, you run into the same problem as when shiftSum was more than chars.length - 1. The solution is to add shiftDiff to chars.length. That's right . . . add. shiftDiff is negative, which means adding the two together yields a number shiftDiff less than chars.length, which is the desired index for deciphering. The code below reflects whether substitute() uses shiftDiff or shiftDiff + chars.length as the index for deciphering:

return (this.chars.charAt((shiftDiff < 0) ? 
  shiftDiff + this.chars.length : shiftDiff));

Different Substitutions for Different Ciphers

We just examined what all of the SubstitutionCiphers have in common--the substitute() method. Now let's take a look at what sets them apart. The SubstitutionCipher constructor expects an argument named algorithm. This argument is not a string, a Boolean, a number, or even an object. This argument is a reference to a function that will implement (call) the substitute() method in a unique way.

For the Caesar cipher, the argument passed in is a reference to function caesarAlgorithm(). The Vigenere cipher, not surprisingly, receives a reference to function vigenereAlgorithm(). Let's look at the functions of each.

Caesar algorithm

The Caesar algorithm is the easier of the two. Lines 81-94 contain the code:

function caesarAlgorithm (data, action) {
  data = this.purify(data);
  if(!data) { 
    alert('No valid text to ' + (action ? 'cipher.' : 'decipher.'));
    return false;
    }
  var shiftIdx = 
  (NN ? refSlide("caesar").document.forms[0].Shift.selectedIndex : 
    document.forms[1].Shift.selectedIndex);
  var cipherData = '';
  for (var i = 0; i < data.length; i++) {
    cipherData += this.substitute(data.charAt(i), shiftIdx, action);
    }
  return cipherData;
  }

The first few lines format the data, then check to see whether there is any qualifying character left over. The string in argument data is formatted by calling purify() and passing in data as the argument. As long as the call to purify() doesn't return false, the cipher continues. See the earlier section on the purify() method for details about the method's return.

The next thing to do is determine the number of characters by which the user wants to shift the text. That's pretty easy. It comes from the select list in the form on the layer named caesar. I haven't mentioned anything about that yet, but you can jump ahead to lines 180-181 if you want to see the call to create both layers. However, the Navigator DOM differs from the Internet Explorer DOM when it comes to accessing form elements in different layers. The select list has the name Shift.

In Navigator, it looks like this:

document.layers['caesar'].document.Shift.selectedIndex

In MSIE, though, it looks like this:

document.forms[1].Shift.selectedIndex

TIP: As you just saw, accessing forms and form elements in layers requires different syntax. The document object model in NN differs from the one in MSIE. This isn't the first time we've seen it in this book. In fact, the majority of code in dhtml.js exists only for creating and manipulating layers in both browsers. Do yourself a favor. Make sure you know when you'll have to accommodate both and when you won't. Until we see a unified DOM, keep the following resources handy.

Microsoft's DHTML Objects:

http://www.microsoft.com/workshop/author/dhtml/reference/
objects.asp

Netscape's Style Sheet Reference and Client-Side JavaScript
Reference:

http://developer1.netscape.com:80/docs/manuals/communicator/dynhtml/jss34.htm and http://developer.netscape.com/docs/manuals/js/client/jsref/index.htm

Variable shiftIdx accounts for that difference by using the NN variable to determine which of the two to access. The call to refSlide() in line 88 is a convenient way to refer to document.layers["caesar"]. Now that shiftIdx has been assigned, caesarAlgorithm() iterates data.length times, calling substitute() each time and concatenating its return to the once-empty local variable cipherData. Argument action is passed in each time to properly indicate to substitute() whether to encipher or decipher. After the last iteration, caesarAlgorithm() returns cipherData, which now contains the properly ciphered string.

That is the simpler of the two cipher algorithms explained. Let's look at vigenereAlgorithm(). The primary difference here is that the argument shiftIdx passed to substitute() in caesarAlgorithm() remains constant. With this function, shiftIdx can (and usually does) change with every call to substitute(). The other difference is that the user chooses a keyword instead of a number. Here are lines 96-119:

function vigenereAlgorithm (data, action) {
  data = this.purify(data);
  if(!data) { 
    alert('No valid text to ' + (action ? 'cipher.' : 'decipher.'));
    return false;
    }
  var keyword = 
    this.purify((NN ? 
      refSlide("vigenere").document.forms[0].KeyWord.value : 
      document.forms[2].KeyWord.value));
  if(!keyword || keyword.match(/\^s+$/) != null) { 
    alert('No valid keyword for ' + 
     (action ? 'ciphering.' : 'deciphering.'));
    return false;
    }
  keyword = keyword.replace(/\s+/g, '');
  var keywordIdx = 0;
  var cipherData = '';
  for (var i = 0; i < data.length; i++) {
    shiftIdx = this.chars.indexOf(keyword.charAt(keywordIdx));
    cipherData += this.substitute(data.charAt(i), shiftIdx, action);
    keywordIdx = (keywordIdx == keyword.length - 1 ? 0 : keywordIdx + 1);
    }
  return cipherData;
  }

The first five lines are the same as in caesarAlgorithm(). They do the same formatting and validating. The next few lines perform similar work on the keyword. The keyword comes from the form field located on the layer named vigenere. Remember that we have to accommodate both Navigator and MSIE DOMs.

In Navigator, it looks like this:

document.layers['vigenere'].document. KeyWord.value

In MSIE, though, it looks like this:

document.forms[2]. KeyWord.value

Variable keyword then is assigned as follows:

var keyword = this.purify((NN ? 
  refSlide("vigenere").document.forms[0].KeyWord.value : 
  document.forms[2].KeyWord.value));

Notice that the purify() method is used again. It is designed for plaintext and ciphertext, but the demands for the keyword are very similar. Since the substitute() method can substitute only characters in chars, the keyword must contain characters from chars as well. Acceptable keywords include people, machines, init2wnit, and 1or2or3. However, using characters not in chars can still be acceptable. Remember that purify() removes all characters that aren't a-z or 0-9, and replaces all newline and carriage return characters and multiple whitespaces with single whitespaces. While the user might enter 1@@#derft as a keyword, purify() formats that string and returns 1derft , and that contains qualifying characters. Now consider a keyword with whitespaces, say all the spaces in between. This contains qualifying characters, except for those whitespaces. Line 110 removes them:

keyword = keyword.replace(/\s+/g, '');

The bottom line is: as long as there is at least one qualifying character in the keyword, that is what will be used in vigenereAlgorithm().

How shiftIdx Changes

The plaintext (or ciphertext) and the keyword have been formatted. All that remains is to substitute each of the characters accordingly. By definition of the Vigenére cipher, each character of text is enciphered or deciphered according to the index of the next character in the keyword. This brings us to lines 111-118:

var keywordIdx = 0;
var cipherData = '';
for (var i = 0; i < data.length; i++) {
  shiftIdx = this.chars.indexOf(keyword.charAt(keywordIdx));
  cipherData += this.substitute(data.charAt(i), shiftIdx, action);
  keywordIdx = (keywordIdx == keyword.length - 1 ? 0 : keywordIdx + 1);
  }
return cipherData;

Using variable keywordIdx starting at 0, we can get the index of each keyword character as follows:

keyword.charAt(keywordIdx)

For each character of data (the plaintext or ciphertext), shiftIdx is set to the index of chars at keyword.charAt(keywordIdx). Variable cipherData is then set equal to itself plus the return of the substitute() method, which receives a fresh copy of data.charAt(i) and shiftIdx, along with action. Incrementing keywordIdx by 1 afterwards sets things up for the next iteration.

Each SubstitutionCipher Is Also a Cipher

Since all ciphers, no matter what kind they are, must have the same basic characteristics, the SubstitutionCipher constructor must inherit all the properties of Cipher. That takes place in one line:

SubstitutionCipher.prototype = new Cipher;

Now each instantiated SubstitutionCipher object has a property called chars and a method called purify(). Every SubstitutionCipher then, is a more specific version of a Cipher.

JavaScript Technique:
Tapping into JavaScript Object Inheritance

As mentioned in the last chapter, JavaScript employs prototype-based inheritance, not class-based inheritance common to languages such as Java. Chapter 8's "The JavaScript Technique: Adding Object Properties" shows you how to add properties such as strings or numbers to existing objects. You can also utilize the prototype property of constructor functions to create inheritance hierarchy. That's what happens in line 65. SubstitutionCipher inherits all the properties of Cipher. This lets you leverage the true power of object-oriented programming (as far as JavaScript is concerned). You can get more information about JavaScript inheritance at Netscape's DevEdge Online at:

http://developer1.netscape.com:80/docs/manuals/communicator/jsobj/contents.htm#1030750

Creating Each Instance of SubstitutionCipher

Up to this point, we've seen how the two ciphers work. Now it's time to examine how to create the objects that represent the two ciphers and how to construct the interface for using them. Creating the objects takes only four lines. Here they are, lines 121-124:

var cipherArray = [
    new SubstitutionCipher("caesar", caesar, caesarAlgorithm), 
    new SubstitutionCipher("vigenere", vigenere, vigenereAlgorithm)
    ];

Variable cipherArray is set to an array. Each of the elements is a SubstitutionCipher. Why put them in an array? The reason is that the application knows which cipher to use according to the OPTION selected in the first select list on the page. We'll cover that in a moment.

JavaScript Technique: Using Alternate Syntax

As of JavaScript 1.2, you can replace code such as:

var myArray = new Array(1,2,3);

with a shortened version like this:

var myArray = [1,2,3];

You can also create objects on the fly as follows. Instead of this:

function myObj() {
  this.name="A New Object";
  this.description = "Old School Object";
  }
 
var objOne = new myObj();

try this:

var myObj = {name: "A New Object", description: "New School Object"};

Notice that the property and method name-value pairs are separated by a comma. Both 4.x versions of MSIE and Navigator support these. Take your pick.

For now, notice that each call to the SubstitutionCipher() constructor passes with it the expected strings, a name and a description, and a reference to a function, which will be assigned to the algorithm property of each SubstitutionCipher object created. That creates the objects. Let's look at the interface. This happens between the BODY tags:

<DIV>
  <TABLE BORDER=0>
    <TR>
      <TD ALIGN=CENTER COLSPAN=3>
      <IMG SRC="images/cipher.jpg"> 
      </TD>
    </TR>
    <TR>
      <TD VALIGN=TOP WIDTH=350>
      <FORM>
      <SELECT NAME="Ciphers" 
        onChange="showCipher(this.options[this.selectedIndex].value);">
      <OPTION VALUE="caesar">Caesar Cipher
      <OPTION VALUE="vigenere">Vigenére Cipher
      </SELECT>
      </TD>
      <TD ALIGN=CENTER>
      <TEXTAREA NAME="Data" ROWS="15" COLS="40" 
        WRAP="PHYSICAL"></TEXTAREA>
      <BR><BR>
      <INPUT TYPE=BUTTON VALUE="Encipher" 
        onClick="routeCipher(this.form.Ciphers.selectedIndex, 
        this.form.Data.value, true);"> 
      <INPUT TYPE=BUTTON VALUE="Decipher" 
        onClick="routeCipher(this.form.Ciphers.selectedIndex, 
        this.form.Data.value, false);">
      <INPUT TYPE=BUTTON VALUE="  Reset  " 
        onClick="this.form.Data.value='';">  
      </FORM>
      </TD>
    </TR>
  </TABLE>
</DIV>

This code creates a two-row table. The top row houses the graphic in a TD with COLSPAN set to 2. The bottom row contains two data cells. The one at the left contains a single select list, and looks like this:

<SELECT NAME="Ciphers" 
  onChange="showCipher(this.options[this.selectedIndex].value);">
<OPTION VALUE="caesar">Caesar Cipher
<OPTION VALUE="vigenere">Vigenére Cipher
</SELECT>

This list determines which cipher interface is currently displayed. Since there are only two, it's either one or the other. The onChange event handler calls the showCipher() function, passing in the value of the option currently selected. This function is pretty short. You'll find it in lines 126-130:

function showCipher(name) {
  hideSlide(curCipher);
  showSlide(name);
  curCipher = name;
  }

The code inside might look familiar. It hails from previous chapters like Chapter 3, The Interactive Slideshow, or Chapter 6. You'll find functions hideSlide() and showSlide() in dhtml.js. Refer to Chapter 3 for detailed coverage.

Notice that the data cell is set to a width of 350 pixels. Other than a select list, that data cell is pretty empty. Fortunately, two layers will fill in that available browser real estate. You can see the calls to create them in lines 180-181. Function genLayer() creates the cipher layers and is also in dhtml.js. This, too, is a function from the past and won't be covered here:

genLayer("caesar", 50, 125, 350, 200, showName, caesar);
genLayer("vigenere", 50, 125, 350, 200, hideName, vigenere);

This creates the text displays for each cipher, along with the additional select list for the Caesar cipher and the text field for the Vigenere cipher. As just mentioned, you can change the option between Caesar cipher and Vigenére cipher in the top select list, which then displays the proper cipher layer.

As for the other data cell in the bottom table row, it contains a text area and three buttons. Here they are again in lines 161-170:

<TEXTAREA NAME="Data" ROWS="15" COLS="40" WRAP="PHYSICAL"></TEXTAREA>
<BR><BR>
<INPUT TYPE=BUTTON VALUE="Encipher" 
  onClick="routeCipher(this.form.Ciphers.selectedIndex, 
  this.form.Data.value, true);"> 
<INPUT TYPE=BUTTON VALUE="Decipher" 
  onClick="routeCipher(this.form.Ciphers.selectedIndex, 
  this.form.Data.value, false);">
<INPUT TYPE=BUTTON VALUE="  Reset  " onClick="this.form.Data.value='';">

The textarea field holds the plain text (or ciphertext). The "Encipher" button causes the text contained within it to be enciphered. It's the reverse for the "Decipher" button. Both call the same function, routeCipher(). Both pass in the value of the textarea field. The difference is that the last argument is true for one and false for the other.

Choosing the Right Cipher

Choosing the right cipher is easy. The correct cipher always corresponds with the index of the top select list in the form and the index of cipherArray. You can see this in routeCipher() shown here:

function routeCipher(cipherIdx, data, action) {
  var response = cipherArray[cipherIdx].algorithm(data, action);
  if(response) { 
    document.forms[0].Data.value = response;
    }
  }

This function accepts three arguments. We've already discussed the last two. data is the text in the textarea, and action is either true or false. The first one, cipherIdx, comes from document.forms[0].Ciphers.selectedIndex. It has to be 0 or 1. Whichever it is, the algorithm() method of the corresponding SubstitutionCipher object in cipherArray gets the call. If algorithm() returns a non-false value, it must be qualified enciphered (or deciphered) text.

A Final Thought

You've probably realized by now, but the code in line 179:

document.forms[0].Ciphers.selectedIndex = 0;

simply resets the selected OPTION in the top select list to the first one. This forces the OPTION selected to match the cipher layer in view, even if the user reloads the page.

Potential Extensions

While this application is cool to play with as is, the next level is to send it in email. You can do that in three easy steps. First, copy the following function, and paste it between your SCRIPT tags:

function sendText(data) {
  paraWidth = 70;
  var iterate = parseInt(data.length / paraWidth);
  var border = '\n-------\n';
  var breakData = '';
  for (var i = 1; i <= iterate; i++) {
    breakData += data.substring((i - 1) * paraWidth, i * paraWidth) + 
      '\r';
    }
  breakData += data.substring((i - 1) * paraWidth, data.length);
  document.CipherMail.Message.value = border + breakData + border;
  document.CipherMail.action = 
    "mailto:someone@somewhere.com\?subject=The Secret Message";
  return true;
  }

This performs some last millisecond formatting before sending the email. The formatting inserts carriage returns every paraWidth characters. This ensures that the email message that the recipient receives isn't one line of text 40 miles long. The next thing to do is add the second form required. Insert this code after the closing FORM tag in the current document:

FORM NAME="CipherMail" ACTION="" METHOD="POST" ENCTYPE="text/plain" 
  onSubmit="return sendText(document.forms[0].Data.value);">
<INPUT TYPE=HIDDEN NAME="Message">
<INPUT TYPE=SUBMIT VALUE="   Send   ">
</FORM>

This form, named CipherMail, contains a lone HIDDEN field. The last thing to do is change the form references in the cipher algorithm functions.

Change lines 87-89:

var shiftIdx = (NN ? 
  refSlide("caesar").document.forms[0].Shift.selectedIndex : 
  document.forms[1].Shift.selectedIndex);

to this:

var shiftIdx = (NN ? 
  refSlide("caesar").document.forms[0].Shift.selectedIndex : 
  document.forms[2].Shift.selectedIndex);

Then lines 102-104 from this:

var keyword = this.purify((NN ? 
  refSlide("vigenere").document.forms[0].KeyWord.value : 
  document.forms[2].KeyWord.value));

to this:

var keyword = this.purify((NN ? 
  refSlide("vigenere").document.forms[0].KeyWord.value : 
document.forms[3].KeyWord.value));

You need to make these changes because you added another form to the hierarchy in the previous step. sendText() sets the value of this hidden field to the value of whatever text is entered in the textarea. sendText() then submits this form, which has the ACTION attribute set to mailto:your_e-mail@your_mail_server.com. Figure 9-7 shows what the message looks like when it arrives. That's the view from my Hotmail account. Upon receipt, the user can cut and paste the text between the dashed lines, then decipher the message with the previously agreed-upon cipher and key. Now your visitors are using encrypted mail, and you're the genius behind it!

Figure 9-7. The encrypted email

 

P.S. This will work only if the user has the NN or MSIE email client correctly configured, which is most likely the case.

P.S.S. \ch09\cipher2.html has the email functionality added.


1. From the U.S. Army Field Manual 34-40-2.

Back to: JavaScript Application Cookbook


oreilly.com Home | O'Reilly Bookstores | How to Order | O'Reilly Contacts
International | About O'Reilly | Affiliated Companies | Privacy Policy

© 2001, O'Reilly & Associates, Inc.