1  /**
     2   * com.sekati.crypt.XOR
     3   * @version 1.2.3
     4   * @author jason m horwitz | sekati.com
     5   * Copyright (C) 2007  jason m horwitz, Sekat LLC. All Rights Reserved.
     6   * Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
     7   * 
     8   * @see http://www.eng.uwaterloo.ca/~ejones/software/xorcrypt12.js
     9   * @see http://www.groovyweb.uklinux.net/index.php?page_name=javascript%20xor%20encrypter
    10   */
    11  
    12  import com.sekati.crypt.ICipher;
    13  
    14  /**
    15   * Encrypt and Decrypt a string with XORCRYPT Version 1.2 algorithm
    16   */
    17  class com.sekati.crypt.XOR implements ICipher {
    18  
    19  	/* Create the encrypt table 
    20  	The last char in the table is always the escape code
    21  	The table is not quite 128 chars, it is 95 (minus the escape char)
    22  	Values 93-127 must be escaped */
    23  	private static var cryptTable:String = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\t!@#$%^&*()`'-=[];,./?_+{}|:<>~";
    24  	//93 Chars
    25  	private static var cryptLength:Number = cryptTable.length - 1;
    26  	// The escape code is ~
    27  	//private var escapeChar:String = cryptTable.charAt(cryptLength);
    28  	//The linefeed char - escaped to double escapeChar
    29  	private static var lineFeed:String = "\n";
    30  	//Double quotes are escaped to ~'
    31  	private static var doubleQuote:String = "\"";
    32  	//The number of ms to wait to clear the status bar message
    33  	private static var clearMessage:Number = new Number( 5000 );
    34  
    35  	// This function uses the key and encrypts a string with the password
    36  	// Encryption strips all linefeeds - but they are replaced upon decrypt
    37  	
    38  	/**
    39  	 * Encrypts a string with the specified key.
    40  	 * @param input (String) string to encrypt
    41  	 * @param password (String) encryption key
    42  	 * @return String 
    43  	 * {@code Usage:
    44  	 *  	var enc:String = XOR.encrypt ("hello world","tooManySecrets");
    45  	 * }
    46  	 */	 
    47  	public static function encrypt(input:String, password:String):String {
    48  		var escapeChar:String = cryptTable.charAt( cryptLength );
    49  		var inChar:String, inValue:Object, outValue:Object;
    50  		var output:String = "";
    51  		var arNumberPw:Array = new Array( );
    52  		var pwLength:Number = password.length;
    53  		var inLength:Number = input.length;
    54  		//var stopStatus:Number = Math.round (inLength / 10);
    55  		//var statusBar:Number = 0;
    56  		var pwIndex:Number;
    57  		//
    58  		for (pwIndex = 0; pwIndex < pwLength ; pwIndex++) {
    59  			arNumberPw[pwIndex] = cryptTable.indexOf( password.charAt( pwIndex ) );
    60  		}
    61  		/* XOR all the chars */
    62  		pwIndex = 0;
    63  		for (var inIndex:Number = 0; inIndex < inLength ; inIndex++, pwIndex++) {
    64  			//Make sure the password index is in range	
    65  			if (pwIndex == pwLength) {
    66  				pwIndex = 0;
    67  			}
    68  			/* Get the input */
    69  			 
    70  			inChar = input.charAt( inIndex );
    71  			inValue = cryptTable.indexOf( inChar );
    72  			/* Conversion/Escaping Sequence */
    73  			// If the outValue is in the character map, encode it
    74  			// If the encoded value is outside the character map, escape it
    75  			// Else convert it to a char
    76  			// If the input char is a linefeed, escape it
    77  			// If the input char is a double quote, escape it
    78  			// If the input char wasn't found, pass it through
    79  			if (inValue != -1) {
    80  				outValue = arNumberPw[pwIndex] ^ inValue;
    81  				if (outValue >= cryptLength) {
    82  					outValue = escapeChar + cryptTable.charAt( Number( outValue ) - cryptLength );
    83  				} else {
    84  					outValue = cryptTable.charAt( Number( outValue ) );
    85  				}
    86  			} else if (inChar == "r") {
    87  				outValue = escapeChar + escapeChar;
    88  				//If it is a 2 char linefeed skip next one
    89  				if (input.charAt( inIndex + 1 ) == "\n") {
    90  					inIndex++;
    91  				}
    92  			} else if (inChar == "\n") {
    93  				outValue = escapeChar + escapeChar;
    94  			} else if (inChar == doubleQuote) {
    95  				outValue = escapeChar + "'";
    96  			} else {
    97  				outValue = inChar;
    98  			}
    99  			output += String( outValue );
   100  		}
   101  		return output;
   102  	}
   103  
   104  	/**
   105  	 * Decrypts a string with the specified key.
   106  	 * @param input (String) string to decrypt
   107  	 * @param password (String) decryption key
   108  	 * @return String
   109  	 * {@code Usage:
   110  	 *  	var dec:String = XOR.decrypt (enc,"tooManySecrets");
   111  	 * } 
   112  	 */
   113  	public static function decrypt(input:String, password:String):String {
   114  		var inChar:String, inValue:Object, outValue:Object, escape:Boolean = false;
   115  		var output:String = "";
   116  		var arNumberPw:Array = new Array( );
   117  		var pwLength:Number = password.length;
   118  		var inLength:Number = input.length;
   119  		//var stopStatus:Number = Math.round (inLength / 10);
   120  		//var statusBar:Number = 0;
   121  		var pwIndex:Number;
   122  		for (pwIndex = 0; pwIndex < pwLength ; pwIndex++) {
   123  			arNumberPw[pwIndex] = cryptTable.indexOf( password.charAt( pwIndex ) );
   124  		}
   125  		/* XOR all the chars */
   126  		pwIndex = 0;
   127  		for (var inIndex:Number = 0; inIndex < inLength ; inIndex++, pwIndex++) {
   128  			if (pwIndex >= pwLength) {
   129  				//Make sure the password index is in range
   130  				pwIndex = 0;
   131  			}
   132  			/* Get the input */
   133  			 
   134  			inChar = input.charAt( inIndex );
   135  			inValue = cryptTable.indexOf( inChar );
   136  			/* Decrypting/Unescaping Sequence */
   137  			// If the input char wasn't found, pass it through (error checking)
   138  			// If the last char was an escapeChar
   139  			//And the input is an escapeChar, output a linefeed
   140  			//Or the input is a single quote, output a double quote
   141  			//Otherwise just add the cryptLength to the inValue
   142  			//Turn escape off
   143  			// If the inValue hasn't been coverted to an outValue yet
   144  			// If the inChar is an escapeChar, turn escape on
   145  			// Otherwise decrypt the encrypted character
   146  			if (inValue == -1) {
   147  				outValue = inChar;
   148  			} else if (escape) {
   149  				if (inValue == cryptLength) {
   150  					outValue = lineFeed;
   151  					inValue = -1;
   152  				} else if (inChar == "'") {
   153  					outValue = doubleQuote;
   154  					inValue = -1;
   155  				} else {
   156  					inValue += cryptLength;
   157  				}
   158  				escape = false;
   159  			} else if (inValue == cryptLength) {
   160  				escape = true;
   161  				pwIndex--;
   162  				//Stop the password from incrementing
   163  				outValue = "";
   164  				inValue = -1;
   165  			}
   166  			if (inValue != -1) {
   167  				outValue = cryptTable.charAt( arNumberPw[pwIndex] ^ inValue );
   168  			}
   169  			/* Output */
   170  			 
   171  			output += String( outValue );
   172  		}
   173  		return output;
   174  	}
   175  
   176  	private function XOR() {
   177  	}
   178  }