/*::.
==================================================================================================================================
=================================================¦ Copyright © 2007 Allen Baker ¦=================================================
                                                 +------------------------------+
File:       LockerUtils.java
Originator: Allen Baker (2007.01.02)
LayoutRev:  5
================================================================================================================================== */



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



/*.
==========================================================================================
Imports
------------------------------------------------------------------------------------------ */
import java.io.*;
import java.util.*;



/*::
======================================================================================================================== *//**
This class provides utility methods for locker files.

<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="LockerUtils.java.html">
               LockerUtils.java
            </A>
         </DD>
      </DT>
      <DT>
         <B>
            Author:
         </B>
         <DD>
            <A href="mailto:sourcecode.v01@cosmicabyss.com">
               Allen Baker
            </A>
         </DD>
      </DT>
   </DL>
*//*
======================================================================================================================== */
public class LockerUtils
   {



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method verifys and/or gets a password for a decryption operation

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> XString decryptionPassword
      (
      LockerPropertiesFile  pPropertiesFile,
      Type                  pPassword
      )
      throws Exception
      {
      /*.
      ==========================================================================================
      If no password was provided, then get one from the user
      ------------------------------------------------------------------------------------------ */
      XString  password;
      if (pPassword == null)
         {
         password = Util.getPassword("WHAT'S THE PASSWORD CHIEF?");
         if (password == null) return null;   // null means the user pressed "cancel"
         }
      else
         {
         password = new XString(pPassword);
         }
      /*.
      ==========================================================================================
      Check the digest file to ensure that the right password and iteration is being used to
      decrypt. If they are not the same as the ones used to ENcrypt the file, then DEcrypting
      with them will produce a corrupt original file.
      ------------------------------------------------------------------------------------------ */
      while ( ! pPropertiesFile.passwordMatches(password))
         {
         password = Util.getPassword
            (
            "THE PASSWORD IS INCORRECT; TRY AGAIN"
            );
         if (password == null) return null;   // null means the user pressed "cancel"
         }
      return password;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method verifys and/or gets a password for an encryption operation<P>

   Once a file is encrypted with a password, it cannot ever be decrypted if the user forgets the
   password. Such an encrypted file is unrecoverable and might as well be deleted.<P>

      (A password recovery mechanism is a security hole, so one is not provided.)<P>

   The user could simply forget the password he used, and in that case, all is lost. However, the user
   could also mistype a password and not ever know what password he used. This program takes every
   possible measure to prevent the user from encrypting a file with an accidently mistyped password.
   The user is unlikely to be able to know what the mistyped password was when he tries to decrypt the
   file. Making the user retype the password to confirm it is one way to try to prevent mistyped
   passwords from being used. This following section of code is the last-ditch defense against mistyped
   passwords. It checks the password that the user typed in and confirmed against a database of
   previously used password hashes. If the password just entered has never been used before, then this
   code alerts the user and makes him type it in one more time. As a result, the first time a password
   is used, the user has to type it in three times. The theory behind this is that a user will usually
   use the same password or a small number of passwords to encrypt all his files and a previously
   unused password is more likely to be a mistyped password than one that has been used before.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> XString encryptionPassword(Type  pPassword) throws Exception
      {
      /*.
      ==========================================================================================
      If no password was provided, then get one from the user
      ------------------------------------------------------------------------------------------ */
      XString  password;
      if (pPassword == null)
         {
         boolean  passwordIsConfirmed = false;
         XString  reEntry             = null;
         password = Util.getPassword("WHAT'S THE PASSWORD CHIEF?");
         if (password == null) return null;   // null means the user pressed "cancel"
         do
            {
            reEntry  = Util.getPassword("CONFIRM BY REENTERING THE PASSWORD");
            if (reEntry == null) return null;   // null means the user pressed "cancel"
            passwordIsConfirmed = password.equals(reEntry);
            if ( ! passwordIsConfirmed)
               {
               password = Util.getPassword("PASSWORDS DO NOT MATCH.  TRY AGAIN");
               if (password == null) return null;   // null means the user pressed "cancel"
               }
            } while ( ! passwordIsConfirmed);
         }
      else
         {
         password = new XString(pPassword);
         }
      /*.
      ==========================================================================================
      Check the previously used passwords to see if this one has ever been used. If not, confirm
      it and add it to the file.
      ------------------------------------------------------------------------------------------ */
      XString  thisPwdHash = passwordHash(password);
      boolean  found       = false;
      /*.
      ==========================================================================================
      Search the previously used passwords to see if this one has been used before
      ------------------------------------------------------------------------------------------ */
      for (XString  usedPwdHash : cUsedPwdHashes)
         {
         found = thisPwdHash.equals(usedPwdHash);
         if (found) break;
         }
      /*.
      ==========================================================================================
      If this password has never been used, make the user confirm it one more time.
      ------------------------------------------------------------------------------------------ */
      if ( ! found)
         {
         boolean  passwordIsConfirmed = false;
         XString  reEntry             = null;
         reEntry  = Util.getPassword("NEVER USED PASSWORD !!  CONFIRM AGAIN");
         if (reEntry == null) return null;   // null means the user pressed "cancel"
         passwordIsConfirmed = password.equals(reEntry);
         /*.
         ==========================================================================================
         If the user doesn't confirm it right, crash and burn to prevent the password from being
         used and to demonstrate the gravity of the situation.
         ------------------------------------------------------------------------------------------ */
         if ( ! passwordIsConfirmed)
            {
            throw new Exception("Mistyped Password");
            }
         /*.
         ==========================================================================================
         Otherwise, it's an intentionally new password so add it to the file of previously used
         passwords.
         ------------------------------------------------------------------------------------------ */
         TextWriter  usedPwdFileWriter = new TextWriter(Const.USEDPWD);
         cUsedPwdHashes.add(thisPwdHash);
         for (XString  usedPwdHash : cUsedPwdHashes)
            {
            usedPwdFileWriter.println(usedPwdHash);
            }
         usedPwdFileWriter.flush();
         usedPwdFileWriter.close();
         }
      /*.
      ==========================================================================================
      ------------------------------------------------------------------------------------------ */
      return password;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method verifies and/or gets an iteration number for a decryption operation.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public <Type> int decryptionIterations
      (
      LockerPropertiesFile  pPropertiesFile,
      Type                  pPassword,
      int                   pIterations
      )
      throws Exception
      {
      /*.
      ==========================================================================================
      Check the digest file to ensure that the right password and iteration is being used to
      decrypt. If they are not the same as the ones used to ENcrypt the file, then DEcrypting
      with them will produce a corrupt original file.
      ------------------------------------------------------------------------------------------ */
      while ( ! pPropertiesFile.iterationMatches(pPassword,pIterations))
         {
         XString s = Util.getPassword
            (
            "ITERATIONS ARE INCORRECT; TRY AGAIN"
            );
         if (s == null) return (-1);   // null means the user pressed "cancel"
         if (s.isAllDigits())
            {
            pIterations = UMath.decodeDecimalInt(s);
            }
         }
      return pIterations;
      }



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

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

   @return
      A reference to this object

   @param
      pConsole is a ConsoleStream through which this objects sends its output.
   *//*
   ---------------------------------------------------------------------------------------------------- */
   public LockerUtils 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="LockerUtils.java.html#004">View source</A>

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public LockerUtils 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(LockerUtils.class.getName());
   private static final int      DFLT_LINE_LEN = ConsoleMessage.defaultLineLength();
   /*.
   ==========================================================================================
   Class variables
      <BLOCKQUOTE>
         <PRE id="unindent">
            cOut           : console output.
            cThis          : is a reference to the singleton that is created on
                             the first call to the constructor.
            cUsedPwdHashes : a list of the hashes of all previously used passwords.
         </PRE>
      </BLOCKQUOTE>
   ------------------------------------------------------------------------------------------ */
   private static ConsoleStream       cOut           = ConsoleStream.getSingleton();
   private static LockerUtils         cThis          = null;
   private static ArrayList<XString>  cUsedPwdHashes = new ArrayList<XString>();
   /*.
   ==========================================================================================
   Instance variables
      iVarName : something or other
   ------------------------------------------------------------------------------------------ */



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method creates a LockerUtils class object.<P>

   The key to creating a singleton is to prevent the client programmer from having any way to create an
   object except the ways you provide. You must make all constructors private, and you must create at
   least one constructor to prevent the compiler from synthesizing a default constructor for you (which
   it will create as “public�?).<P>

   At this point, you decide how you’re going to create your object. Here, it’s created on demand the
   first time a client programmer asks for one, but you can also create it statically. In any case, the
   object should be stored privately. You provide access through public methods. Here, getSingleton()
   produces the reference to the Singleton object. The rest of the interface is a regular class
   interface.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   private LockerUtils() throws Exception
      {
      /*.
      ==========================================================================================
      We keep a file of the hashes of previously used passwords. Open it up and read all the
      hashes into an array.
      ------------------------------------------------------------------------------------------ */
      TextReaderIterator  usedPwdFileIter = new TextReaderIterator(Const.USEDPWD);
      while (usedPwdFileIter.hasNext())
         {
         cUsedPwdHashes.add(usedPwdFileIter.next());
         }
      }



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



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



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   The key to creating a singleton is to prevent the client programmer from having any way to create an
   object except the ways you provide. You must make all constructors private, and you must create at
   least one constructor to prevent the compiler from synthesizing a default constructor for you (which
   it will create as “public�?).<P>

   At this point, you decide how you’re going to create your object. Here, it’s created on demand the
   first time a client programmer asks for one, but you can also create it statically. In any case, the
   object should be stored privately. You provide access through public methods. Here, getSingleton()
   produces the reference to the Singleton object. The rest of the interface is a regular class
   interface.

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static LockerUtils getSingleton() throws Exception
      {
      if (cThis == null)
         {
         cThis = new LockerUtils();
         }
      return cThis;
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method hashes a password

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static <Type> XString passwordHash(Type pPassword) throws Exception
      {
      return // UString.asciiHash
         (
         (new Digest("COMPOUND",pPassword)).xstring().asciiHash()
         );
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   This method hashes an interation count

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

   *//*
   ---------------------------------------------------------------------------------------------------- */
   public static <Type> XString iterationHash(Type pPassword, int pIterations) throws Exception
      {
      return // UString.asciiHash
         (
         (new Digest("COMPOUND",UString.toString(pPassword) + ":" + pIterations)).xstring().asciiHash()
         );
      }



   /*:                                    
   ====================================================================================================
   [][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][][]
   ==================================================================================================== *//**
   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.LockerUtils
            </DD>
         </DT>
      </DL>

   <P><B>Implementation: </B><A HREF="LockerUtils.java.html#009">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
      ------------------------------------------------------------------------------------------ */
      LockerUtils  obj = LockerUtils.getSingleton();
      /*.
      ==========================================================================================
      Test code
      ------------------------------------------------------------------------------------------ */
      obj.test();


      cOut.println(LockerUtils.passwordHash(""               ));
      cOut.println(LockerUtils.passwordHash("1"              ));
      cOut.println(LockerUtils.passwordHash("ABCDEDCBA"      ));
      cOut.println(LockerUtils.passwordHash("My Password"    ));
      cOut.println(LockerUtils.passwordHash("sxx!@#$%^&*()Ly"));

      cOut.println();
      cOut.println(LockerUtils.passwordHash("Allen"));
      cOut.println(LockerUtils.iterationHash("Allen",2));
      cOut.println(LockerUtils.iterationHash("Allen",4));


      }



   }  // class LockerUtils



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