/*::.
==================================================================================================================================
=================================================¦ Copyright © 2007 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       RAMCipher.java
Originator: Allen Baker (2007.05.27 21:02)
LayoutRev:  5
================================================================================================================================== */



/*.
==========================================================================================
Package
------------------------------------------------------------------------------------------ */
package cosmicabyss.com.lib;



/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.util.*;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;



/*::
======================================================================================================================== *//**
This class encrypts/decrypts data in memory.

<P>
   <DL>
      <DT>
         <B>
            Example usage:
         </B>
         <DD>
            <BLOCKQUOTE>
               <PRE id="unindent">
                  no example provided
               </PRE>
            </BLOCKQUOTE>
         </DD>
      </DT>
      <DT>
         <B>
            View Source:
         </B>
         <DD>
            <A href="RAMCipher.java.html">
               rAMCipher.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class RAMCipher extends BaseCipher
   {



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the RAMCipher class.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#000">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public RAMCipher() throws Exception
      {
      super();
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the RAMCipher class.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#001">View source</A>

   @param
      pPassword is the password that will be used to encrypt and/or decrypt the data.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public RAMCipher
      (
      Password  pPassword
      )
      throws Exception
      {
      super(pPassword);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates an instance of the RAMCipher class.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#002">View source</A>

   @param
      pPassword is the password that will be used to encrypt and/or decrypt the data.
   @param
      pCipherIterations is the number of times the data will be run through the encryption and/or
      decryption algorithm when the encrypt and/or decrypt method is called. If this value is 1 or
      less, the data will be run through 1 time.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public RAMCipher
      (
      Password  pPassword,
      int       pCipherIterations
      )
      throws Exception
      {
      super(pPassword,pCipherIterations);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts a XString.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#003">View source</A>

   @return
      The encrypted XString as a byte array
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public byte[] encrypt(XString pData) throws Exception
      {
      byte[]  data = pData.getBytes();
      return this.encrypt(data);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts the data.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#004">View source</A>

   @return
      The encrypted data
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public byte[] encrypt(byte[] pData) throws Exception
      {
      /*.
      ==========================================================================================
      Make a local copy of the data
      ------------------------------------------------------------------------------------------ */
      byte[]  data = new byte[pData.length];
      for (int h=0; h<pData.length; h++) data[h] = pData[h];
      /*.
      ==========================================================================================
      Get the parameters to use for each iteration
      ------------------------------------------------------------------------------------------ */
      Queue<XString>  passwordQueue  = this.passwordQueue();
      Queue<XString>  saltQueue      = this.saltQueue();
      Queue<XString>  algorithmQueue = this.algorithmQueue();
      /*.
      ==========================================================================================
      Encrypt the data iCipherIterations times
      ------------------------------------------------------------------------------------------ */
      for (int i=0; i<iCipherIterations; i++)
         {
         /*.
         ==========================================================================================
         Get the password, salt, and algorithm for this iteration
         ------------------------------------------------------------------------------------------ */
         XString pwd = passwordQueue.next();
         XString slt = saltQueue.next();
         XString alg = algorithmQueue.next();
         /*.
         ==========================================================================================
         Encrypt the data
         ------------------------------------------------------------------------------------------ */
         data = this.encrypt
            (
            data,
            pwd,
            slt,
            alg
            );
         }
      return data;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts the data.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#005">View source</A>

   @return
      The decrypted data
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public byte[] decrypt(byte[] pData) throws Exception
      {
      /*.
      ==========================================================================================
      Make a local copy of the data
      ------------------------------------------------------------------------------------------ */
      byte[]  data = new byte[pData.length];
      for (int h=0; h<pData.length; h++) data[h] = pData[h];
      /*.
      ==========================================================================================
      Get the parameters to use for each iteration
      ------------------------------------------------------------------------------------------ */
      Stack<XString>  passwordStack  = this.passwordStack();
      Stack<XString>  saltStack      = this.saltStack();
      Stack<XString>  algorithmStack = this.algorithmStack();
      /*.
      ==========================================================================================
      Decrypt the data iCipherIterations times
      ------------------------------------------------------------------------------------------ */
      for (int i=0; i<iCipherIterations; i++)
         {
         /*.
         ==========================================================================================
         Get the password, salt, and algorithm for this iteration
         ------------------------------------------------------------------------------------------ */
         XString pwd = passwordStack.pop();
         XString slt = saltStack.pop();
         XString alg = algorithmStack.pop();
         /*.
         ==========================================================================================
         Decrypt the data
         ------------------------------------------------------------------------------------------ */
         data = this.decrypt
            (
            data,
            pwd,
            slt,
            alg
            );
         }
      return data;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method sets the ConsoleStream for this object.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#006">View source</A>

   @return
      A reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its output.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public RAMCipher setOut(ConsoleStream pConsole)
      {
      cOut = pConsole;
      return this;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This boilerplate method is used for testing an instantiated object of this class and may include any
   code the developer chooses.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#007">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public RAMCipher test() throws Exception
      {
      cOut.titledPrintf
         (
         "\"HELLO WORLD!\"",
         "%s  %s  %s",
         "I'm an object of the", CLASS_NAME, "class, and I approved this message."
         );
      return this;
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Protected  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Private  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*.
   ==========================================================================================
   Class Constants
      CLASS_NAME:
         The name of this class
      DFLT_LINE_LEN:
         The default line length for word wrapping
   ------------------------------------------------------------------------------------------ */
   private static final XString  CLASS_NAME    = new XString(RAMCipher.class.getName());
   private static final int      DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
   /*.
   ==========================================================================================
   Class variables
      cOut:
         Console output.
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream  cOut = ConsoleStream.getSingleton();



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method encrypts data using password-based encryption.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#008">View source</A>

   @return
      The encrypted data

   @param
      pData is the data that will be encrypted.
   @param
      pPassword is the password that will be used to encrypt the data. The same password must be used
      to decrypt the data.
   @param
      pSalt is a random XString that the PBE cipher uses to randomize the key. It's a bit like a second
      password; and in fact, the user can treat it as exactly that -- a second password..
   @param
      pAlgorithnm is the name of the cipher algorithm that will be used to encrypt the data. An example
      of a valid value for pAlgorithm is: "PBEWithMD5AndDES"
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private byte[] encrypt
      (
      byte[]   pData,
      XString  pPassword,
      XString  pSalt,
      XString  pAlgorithm
      )
      throws Exception
      {
      /*.
      ==========================================================================================
      Warn the user if the salt and password are the same
      ------------------------------------------------------------------------------------------ */
      if (pPassword.equals(pSalt))
         {
         cOut.println(Const.WARNING,"Salt matches password");
         }
      /*.
      ==========================================================================================
      Create a cipher object that will encrypt the data
      ------------------------------------------------------------------------------------------ */
      Cipher  cipher = this.encryptionCipher(pPassword,pSalt,pAlgorithm);
      /*.
      ==========================================================================================
      Encrypt it and return the encrypted results
      ------------------------------------------------------------------------------------------ */
      return cipher.doFinal(pData);
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method decrypts data using password-based decryption.

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#009">View source</A>

   @return
      The decrypted data

   @param
      pData is the data that will be decrypted.
   @param
      pPassword is the password that will be used to decrypt the data. It must be the same as the
      password that was used to encrypt the data.
   @param
      pSalt is a random XString that the PBE cipher uses to randomize the key. It's a bit like a second
      password; and in fact, the user can treat it as exactly that -- a second password..
   @param
      pAlgorithnm is the name of the cipher algorithm that will be used to decrypt the data. An example
      of a valid value for pAlgorithm is: "PBEWithMD5AndDES"
   *//*
   ---------------------------------------------------------------------------------------------------- */
   private byte[] decrypt
      (
      byte[]   pData,
      XString  pPassword,
      XString  pSalt,
      XString  pAlgorithm
      )
      throws Exception
      {
      /*.
      ==========================================================================================
      Create a cipher object that will decrypt the data
      ------------------------------------------------------------------------------------------ */
      Cipher  cipher = this.decryptionCipher(pPassword,pSalt,pAlgorithm);
      /*.
      ==========================================================================================
      Decrypt it and return the decrypted results
      ------------------------------------------------------------------------------------------ */
      return cipher.doFinal(pData);
      }



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Inner Classes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Public Static Methods  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method allows this class file to be unit tested as a standalone application. It's the method
   that's called when the class is invoked from the command line by using the java application launcher
   - "java". Main() is not a required method, but the practice of putting one in each class and
   wrapping class test code within it allows easy unit testing of the class; and main does not need to
   be removed when testing is complete.

   <P>
      <DL>
         <DT>
            <B>
               Command line usage:
            </B>
            <DD>
               Java cosmicabyss.com.lib.RAMCipher
            </DD>
         </DT>
      </DL>

   <P><B>Implementation: </B><A HREF="RAMCipher.java.html#010">View source</A>

   @param
      pArgs contains the command line arguments with which this class was invoked as an application.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static void main(String[] pArgs) throws Exception
      {
      /*.
      ==========================================================================================
      Greetings !
      ------------------------------------------------------------------------------------------ */
      cOut.banner(CLASS_NAME);
      /*.
      ==========================================================================================
      Create an object and send its output to the ConsoleStream
      ------------------------------------------------------------------------------------------ */
      RAMCipher  obj = new RAMCipher();
      /*.
      ==========================================================================================
      Test code
      ------------------------------------------------------------------------------------------ */
      obj.test();
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      byte[]     bytes;
      XString    line;
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      line = new XString((pArgs.length<1)? "this is a test" : pArgs[0]);
      cOut.println("Original Text", line);
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      bytes = obj.encrypt(line);
      cOut.println("Encrypted Text", new XString(bytes));
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      bytes = obj.decrypt(bytes);
      cOut.println("Decrypted Text", new XString(bytes));
      }



   }  // class RAMCipher



   /*:.
   ==============================================================================================================
   @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[  Notes  ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
   ============================================================================================================== */