unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#9747: C-x h TAB and M-x untabify
@ 2011-10-13 23:27 Jeffrey Walton
  2011-10-19 23:32 ` Juri Linkov
  0 siblings, 1 reply; 3+ messages in thread
From: Jeffrey Walton @ 2011-10-13 23:27 UTC (permalink / raw)
  To: 9747

[-- Attachment #1: Type: text/plain, Size: 513 bytes --]

I often use C-x h TAB and M-x untabify to format C, C++, and Java code.

If a document has an errant UTF-8 byte order mark (a UTF-8 BOM is EF
BB BF), Emacs cannot always format the source file.

For example, the attached Java file (JavaEncryptor.java-backup) has
1845 BOMs sprinkled throughout. I'm not sure what editor put them in,
but Emacs does not properly handle some operations with them present.
If I strip the errant BOMs with the attached program
(efbbbf-strip.cpp), Emacs will properly format the file.

[-- Attachment #2: JavaEncryptor.java-backup --]
[-- Type: application/octet-stream, Size: 63812 bytes --]

/**
 * OWASP Enterprise Security API (ESAPI)
 *
 * This file is part of the Open Web Application Security Project (OWASP)
 * Enterprise Security API (ESAPI) project. For details, please see
 * <a href="http://www.owasp.org/index.php/ESAPI">http://www.owasp.org/index.php/ESAPI</a>.
 *
 * Copyright (c) 2007 - The OWASP Foundation
 *
 * The ESAPI is published by OWASP under the BSD license. You should read and accept the
 * LICENSE before you use, modify, and/or redistribute this software.
 *
 * @author Jeff Williams <a href="http://www.aspectsecurity.com">Aspect Security</a>
 * @author kevin.w.wall@gmail.com
 * @created 2007
 */
package org.owasp.esapi.reference.crypto;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
// import javax.crypto.Mac;      // Uncomment if computeHMAC() is included.
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;

import org.owasp.esapi.ESAPI;
import org.owasp.esapi.EncoderConstants;
import org.owasp.esapi.Encryptor;
import org.owasp.esapi.Logger;
import org.owasp.esapi.codecs.Hex;
import org.owasp.esapi.crypto.CipherSpec;
import org.owasp.esapi.crypto.CipherText;
import org.owasp.esapi.crypto.CryptoHelper;
import org.owasp.esapi.crypto.KeyDerivationFunction;
import org.owasp.esapi.crypto.PlainText;
import org.owasp.esapi.crypto.SecurityProviderLoader;
import org.owasp.esapi.errors.ConfigurationException;
import org.owasp.esapi.errors.EncryptionException;
import org.owasp.esapi.errors.IntegrityException;
import org.owasp.esapi.reference.DefaultSecurityConfiguration;

/**
 * Reference implementation of the {@code Encryptor} interface. This implementation
 * layers on the JCE provided cryptographic package. Algorithms used are
 * configurable in the {@code ESAPI.properties} file. The main property
 * controlling the selection of this class is {@code ESAPI.Encryptor}. Most of
 * the other encryption related properties have property names that start with
 * the string "Encryptor.".
 *
 * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) <a
 *         href="http://www.aspectsecurity.com">Aspect Security</a>
 * @author kevin.w.wall@gmail.com
 * @author Chris Schmidt (chrisisbeef .at. gmail.com)
 * @since June 1, 2007; some methods since ESAPI Java 2.0
 * @see org.owasp.esapi.Encryptor
 */
public final class JavaEncryptor implements Encryptor {
    private static volatile Encryptor singletonInstance;

    // Note: This double-check pattern only works because singletonInstance
    //       is declared to be volatile.  Usually this method is called
    //       via ESAPI.encryptor() rather than directly.
    public static Encryptor getInstance() throws EncryptionException {
	if ( singletonInstance == null ) {
	    synchronized ( JavaEncryptor.class ) {
		if ( singletonInstance == null ) {
		    singletonInstance = new JavaEncryptor();
		}
	    }
	}
	return singletonInstance;
    }

    private static boolean initialized = false;

    // encryption
    private static SecretKeySpec secretKeySpec = null; // DISCUSS: Why static? Implies one key?!?
    private static String encryptAlgorithm = "AES";
    private static String encoding = "UTF-8";
    private static int encryptionKeyLength = 128;

    // digital signatures
    private static PrivateKey privateKey = null;
        private static PublicKey publicKey = null;
        private static String signatureAlgorithm = "SHA1withDSA";
    private static String randomAlgorithm = "SHA1PRNG";
        private static int signatureKeyLength = 1024;
      
	    // hashing
	    private static String hashAlgorithm = "SHA-512";
        private static int hashIterations = 1024;
      
	    // Logging - DISCUSS: This "sticks" us with a specific logger to whatever it was when
	    //            this class is first loaded. Is this a big limitation? Since there
	    //                    is no method to reset it, we may has well make it 'final' also.
	    private static Logger logger = ESAPI.getLogger("JavaEncryptor");
        // Used to print out warnings about deprecated methods.
	    private static int encryptCounter = 0;
        private static int decryptCounter = 0;
        // DISCUSS: OK to not have a property for this to set the frequency?
	    //          The desire is to persuade people to move away from these
	    //          two deprecated encrypt(String) / decrypt(String) methods,
	    //          so perhaps the annoyance factor of not being able to
	    //          change it will help. For now, it is just hard-coded here.
	    //          We could be mean and just print a warning *every* time.
	    private static final int logEveryNthUse = 25;
      
	    // *Only* use this string for user messages for EncryptionException when
    // decryption fails. This is to prevent information leakage that may be
    // valuable in various forms of ciphertext attacks, such as the
	    // Padded Oracle attack described by Rizzo and Duong.
	private static final String DECRYPTION_FAILED =
	"Decryption failed; see logs for details.";

    // # of seconds that all failed decryption attempts will take. Used to
    // help prevent side-channel timing attacks.
    private static int N_SECS = 2;

        // Load the preferred JCE provider if one has been specified.
	    static {
	      try {
	    SecurityProviderLoader.loadESAPIPreferredJCEProvider();
	} catch (NoSuchProviderException ex) {
	      // Note that audit logging is done elsewhere in called method.
		logger.fatal(Logger.SECURITY_FAILURE,
			     "JavaEncryptor failed to load preferred JCE provider.", ex);
	    throw new ExceptionInInitializerError(ex);
	}
	setupAlgorithms();
	    }
      
    /**
     * Generates a new strongly random secret key and salt that can be
     * copy and pasted in the <b>ESAPI.properties</b> file.
     *
     * @param args Set first argument to "-print" to display available algorithms on standard output.
     * @throws java.lang.Exception  To cover a multitude of sins, mostly in configuring ESAPI.properties.
     */
	public static void main( String[] args ) throws Exception {
	    System.out.println( "Generating a new secret master key" );
	    
	        // print out available ciphers
	        if ( args.length == 1 && args[0].equalsIgnoreCase("-print" ) ) {
	          System.out.println( "AVAILABLE ALGORITHMS" );

	          Provider[] providers = Security.getProviders();
	          TreeMap<String, String> tm = new TreeMap<String, String>();
	          // DISCUSS: Note: We go through multiple providers, yet nowhere do I
		      //      see where we print out the PROVIDER NAME. Not all providers
		      //      will implement the same algorithms and some "partner" with
		      //      whom we are exchanging different cryptographic messages may
		      //      have _different_ providers in their java.security file. So
		      //      it would be useful to know the provider name where each
		      //      algorithm is implemented. Might be good to prepend the provider
		      //      name to the 'key' with something like "providerName: ". Thoughts?
		      for (int i = 0; i != providers.length; i++) {
		        // DISCUSS: Print security provider name here???
		            // Note: For some odd reason, Provider.keySet() returns
		            //     Set<Object> of the property keys (which are Strings)
		            //     contained in this provider, but Set<String> seems
		            //     more appropriate. But that's why we need the cast below.
		            System.out.println("===== Provider " + i + ":" + providers[i].getName() + " ======");
		        Iterator<Object> it = providers[i].keySet().iterator();
		        while (it.hasNext()) {
		              String key = (String)it.next();
		              String value = providers[i].getProperty( key );
		              tm.put(key, value);
		              System.out.println("\t\t   " + key + " -> "+ value );
		            }
		      }

	          Set< Entry<String,String> > keyValueSet = tm.entrySet();
	          Iterator<Entry<String, String>> it = keyValueSet.iterator();
	          while( it.hasNext() ) {
		        Map.Entry<String,String> entry = it.next();
		        String key = entry.getKey();
		        String value = entry.getValue();
		        System.out.println( "   " + key + " -> "+ value );
		      }
	        } else {
	            // Used to print a similar line to use '-print' even when it was specified.
		      System.out.println( "\tuse '-print' to also show available crypto algorithms from all the security providers" );
	        }
	    
	        // setup algorithms -- Each of these have defaults if not set, although
	        //             someone could set them to something invalid. If
	        //             so a suitable exception will be thrown and displayed.
	        encryptAlgorithm = ESAPI.securityConfiguration().getEncryptionAlgorithm();
	    encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength();
	    randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm();

	    SecureRandom random = SecureRandom.getInstance(randomAlgorithm);
	    SecretKey secretKey = CryptoHelper.generateSecretKey(encryptAlgorithm, encryptionKeyLength);
	    byte[] raw = secretKey.getEncoded();
	    byte[] salt = new byte[20];  // Or 160-bits; big enough for SHA1, but not SHA-256 or SHA-512.
					       random.nextBytes( salt );
	    String eol = System.getProperty("line.separator", "\n"); // So it works on Windows too.
	    System.out.println( eol + "Copy and paste these lines into your ESAPI.properties" + eol);
	    System.out.println( "#==============================================================");
	    System.out.println( "Encryptor.MasterKey=" + ESAPI.encoder().encodeForBase64(raw, false) );
	    System.out.println( "Encryptor.MasterSalt=" + ESAPI.encoder().encodeForBase64(salt, false) );
	    System.out.println( "#==============================================================" + eol);
    }
      

    /**
     * Private CTOR for {@code JavaEncryptor}, called by {@code getInstance()}.
     * @throws EncryptionException if can't construct this object for some reason.
     *           Original exception will be attached as the 'cause'.
     */
	private JavaEncryptor() throws EncryptionException {
	byte[] salt = ESAPI.securityConfiguration().getMasterSalt();
	byte[] skey = ESAPI.securityConfiguration().getMasterKey();

	assert salt != null : "Can't obtain master salt, Encryptor.MasterSalt";
	assert salt.length >= 16 : "Encryptor.MasterSalt must be at least 16 bytes. " +
	    "Length is: " + salt.length + " bytes.";
	assert skey != null : "Can't obtain master key, Encryptor.MasterKey";
	assert skey.length >= 7 : "Encryptor.MasterKey must be at least 7 bytes. " +
	    "Length is: " + skey.length + " bytes.";

	// Set up secretKeySpec for use for symmetric encryption and decryption,
	// and set up the public/private keys for asymmetric encryption /
	// decryption.
	// TODO: Note: If we dump ESAPI 1.4 crypto backward compatibility,
	//       then we probably will ditch the Encryptor.EncryptionAlgorithm
	//       property. If so, encryptAlgorithm should probably use
	//       Encryptor.CipherTransformation and just pull off the cipher
	//       algorithm name so we can use it here.
	synchronized(JavaEncryptor.class) {
	    if ( ! initialized ) {
		//
		// For symmetric encryption
		//
		//      NOTE: FindBugs complains about this
		//            (ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD) but
		//            it should be OK since it is synchronized and only
		//            done once. While we could separate this out and
		//            handle in a static initializer, it just seems to
		//            fit better here.
		secretKeySpec = new SecretKeySpec(skey, encryptAlgorithm );

		//
		// For asymmetric encryption (i.e., public/private key)
		//
		try {
		    SecureRandom prng = SecureRandom.getInstance(randomAlgorithm);

		    // Because hash() is not static (but it could be were in not
		    // for the interface method specification in Encryptor), we
		    // cannot do this initialization in a static method or static
		    // initializer.
		    byte[] seed = hash(new String(skey, encoding),new String(salt, encoding)).getBytes(encoding);
		    prng.setSeed(seed);
		    initKeyPair(prng);
		} catch (Exception e) {
		    throw new EncryptionException("Encryption failure", "Error creating Encryptor", e);
		}             

		// Mark everything as initialized.
		initialized = true;
	    }
	}
    }



      /**
       * {@inheritDoc}
       *
          * Hashes the data with the supplied salt and the number of iterations specified in
          * the ESAPI SecurityConfiguration.
          */
	  public String hash(String plaintext, String salt) throws EncryptionException {
	    return hash( plaintext, salt, hashIterations );
	  }
      
	  /**
	   * {@inheritDoc}
	   *
	      * Hashes the data using the specified algorithm and the Java MessageDigest class. This method
	      * first adds the salt, a separator (":"), and the data, and then rehashes the specified number of iterations
	      * in order to help strengthen weak passwords.
	      */
	  public String hash(String plaintext, String salt, int iterations) throws EncryptionException {
	    byte[] bytes = null;
	    try {
	          MessageDigest digest = MessageDigest.getInstance(hashAlgorithm);
	          digest.reset();
	          digest.update(ESAPI.securityConfiguration().getMasterSalt());
	          digest.update(salt.getBytes(encoding));
	          digest.update(plaintext.getBytes(encoding));

	          // rehash a number of times to help strengthen weak passwords
		      bytes = digest.digest();
	          for (int i = 0; i < iterations; i++) {
		        digest.reset();
		        bytes = digest.digest(bytes);
		      }
	          String encoded = ESAPI.encoder().encodeForBase64(bytes,false);
	          return encoded;
	        } catch (NoSuchAlgorithmException e) {
	          throw new EncryptionException("Internal error", "Can't find hash algorithm " + hashAlgorithm, e);
	        } catch (UnsupportedEncodingException ex) {
	          throw new EncryptionException("Internal error", "Can't find encoding for " + encoding, ex);
	        }
	  }
      
	  /**
	        * Convenience method that encrypts plaintext strings the new way (default
	        * is CBC mode and PKCS5 padding). This encryption method uses the master
	        * encryption key specified by the {@code Encryptor.MasterKey} property
	        * in {@code ESAPI.properties}.
	        *
	        * @param plaintext  A String to be encrypted
	        * @return  A base64-encoded combination of IV + raw ciphertext
	        * @throws EncryptionException  Thrown when something goes wrong with the
	        *                 encryption.
	        *
	        * @see org.owasp.esapi.Encryptor#encrypt(PlainText)
	        */
	  @Deprecated public String encrypt(String plaintext) throws EncryptionException
								       {
	logWarning("encrypt", "Calling deprecated encrypt() method.");
	    CipherText ct = null;
	    ct = encrypt(new PlainText(plaintext) );
	    return ct.getEncodedIVCipherText();
	  }


      /**
	   * {@inheritDoc}
	   */
	   public CipherText encrypt(PlainText plaintext) throws EncryptionException {
	     // Now more of a convenience function for using the master key.
	         return encrypt(secretKeySpec, plaintext);
	   }
       
	   /**
	          * {@inheritDoc}
	          */
	   public CipherText encrypt(SecretKey key, PlainText plain)
	         throws EncryptionException
			   {
	     byte[] plaintext = plain.asBytes();
	     boolean overwritePlaintext = ESAPI.securityConfiguration().overwritePlainText();
	     assert key != null : "(Master) encryption key may not be null";
	     
	         boolean success = false;  // Used in 'finally' clause.
					          String xform = null;
	     int keySize = key.getEncoded().length * 8;  // Convert to # bits

							   try {
							       xform = ESAPI.securityConfiguration().getCipherTransformation();
							       String[] parts = xform.split("/");
							       assert parts.length == 3 : "Malformed cipher transformation: " + xform;
							       String cipherMode = parts[1];

							       // This way we can prevent modes like OFB and CFB where the IV should never
							       // be repeated with the same encryption key (at least until we support
							       // Encryptor.ChooseIVMethod=specified and allow us to specify some mechanism
							       // to ensure the IV will never be repeated (such as a time stamp or other
							       // monotonically increasing function).
							       // DISCUSS: Should we include the permitted cipher modes in the exception msg?
							       if ( ! CryptoHelper.isAllowedCipherMode(cipherMode) ) {
								   throw new EncryptionException("Encryption failure: invalid cipher mode ( " + cipherMode + ") for encryption",
												 "Encryption failure: Cipher transformation " + xform + " specifies invalid " +
												 "cipher mode " + cipherMode);
							       }

							              // Note - Cipher is not thread-safe so we create one locally
								          //        Also, we need to change this eventually so other algorithms can
								          //        be supported. Eventually, there will be an encrypt() method that
								          //        takes a (new class) CryptoControls, as something like this:
								          //          public CipherText encrypt(CryptoControls ctrl, SecretKey skey, PlainText plaintext)
								          //        and this method will just call that one.
								          Cipher encrypter = Cipher.getInstance(xform);
							              String cipherAlg = encrypter.getAlgorithm();
							              int keyLen = ESAPI.securityConfiguration().getEncryptionKeyLength();

							              // DISCUSS: OK, what do we want to do here if keyLen != keySize? If use keyLen, encryption
								          //         could fail with an exception, but perhaps that's what we want. Or we may just be
								          //       OK with silently using keySize as long as keySize >= keyLen, which then interprets
								          //       ESAPI.EncryptionKeyLength as the *minimum* key size, but as long as we have something
								          //       stronger it's OK to use it. For now, I am just going to log warning if different, but use
								          //       keySize unless keySize is SMALLER than ESAPI.EncryptionKeyLength, in which case I'm going
								          //       to log an error.
								          //
								          //       IMPORTANT NOTE:  When we generate key sizes for both DES and DESede the result of
								          //                SecretKey.getEncoding().length includes the TRUE key size (i.e.,
								          //                *with* the even parity bits) rather than the EFFECTIVE key size
								          //                (which incidentally is what KeyGenerator.init() expects for DES
								          //                and DESede; duh! Nothing like being consistent). This leads to
								          //                the following dilemma:
								          //
								          //                          EFFECTIVE Key Size    TRUE Key Size
								          //                          (KeyGenerator.init())  (SecretKey.getEncoding().length)
								          //                  ====================================================================
								          //                  For DES:     56 bits                    64 bits
								          //                  For DESede: 112 bits / 168 bits    192 bits (always)
								          //
								          //                We are trying to automatically determine the key size from SecretKey
								          //                based on 8 * SecretKey.getEncoding().length, but as you can see, the
								          //                2 key 3DES and the 3 key 3DES both use the same key size (192 bits)
								          //                regardless of what is passed to KeyGenerator.init(). There are no advertised
								          //                methods to get the key size specified by the init() method so I'm not sure how
								          //                this is actually working internally. However, it does present a problem if we
								          //                wish to communicate the 3DES key size to a recipient for later decryption as
								          //                they would not be able to distinguish 2 key 3DES from 3 key 3DES.
								          //
								          //                The only workaround I know is to pass the explicit key size down. However, if
								          //                we are going to do that, I'd propose passing in a CipherSpec object so we could
								          //                tell what cipher transformation to use as well instead of just the key size. Then
								          //                we would extract keySize from the CipherSpec object of from the SecretKey object.
								          //
								          if ( keySize != keyLen ) {
								            // DISCUSS: Technically this is not a security "failure" per se, but not really a "success" either.
								                logger.warning(Logger.SECURITY_FAILURE, "Encryption key length mismatch. ESAPI.EncryptionKeyLength is " +
											                    keyLen + " bits, but length of actual encryption key is " + keySize +
											                    " bits.  Did you remember to regenerate your master key (if that is what you are using)???");
								          }
							              // DISCUSS: Reconsider these warnings. If thousands of encryptions are done in tight loop, no one needs
								          //          more than 1 warning. Should we do something more intelligent here?
								          if ( keySize < keyLen ) {
								            // ESAPI.EncryptionKeyLength defaults to 128, but that means that we could not use DES (as weak as it
								                // is), even for legacy code. Therefore, this has been changed to simple log a warning rather than
								                //  throw the following exception.
								                //         throw new ConfigurationException("Actual key size of " + keySize + " bits smaller than specified " +
								                //              "encryption key length (ESAPI.EncryptionKeyLength) of " + keyLen + " bits.");
								                logger.warning(Logger.SECURITY_FAILURE, "Actual key size of " + keySize + " bits SMALLER THAN specified " +
											                    "encryption key length (ESAPI.EncryptionKeyLength) of " + keyLen + " bits with cipher algorithm " + cipherAlg);
								          }
							              if ( keySize < 112 ) {    // NIST Special Pub 800-57 considers 112-bits to be the minimally safe key size from 2010-2030.
								                // Note that 112 bits 'just happens' to be size of 2-key Triple DES!
								                logger.warning(Logger.SECURITY_FAILURE, "Potentially unsecure encryption. Key size of " + keySize + "bits " +
											       "not sufficiently long for " + cipherAlg + ". Should use appropriate algorithm with key size " +
											       "of *at least* 112 bits except when required by legacy apps. See NIST Special Pub 800-57.");
								          }
							              // Check if algorithm mentioned in SecretKey is same as that being used for Cipher object.
								          // They should be the same. If they are different, things could fail. (E.g., DES and DESede
								          // require keys with even parity. Even if key was sufficient size, if it didn't have the correct
								          // parity it could fail.)
								          //
								          String skeyAlg = key.getAlgorithm();
							              if ( !( cipherAlg.startsWith( skeyAlg + "/" ) || cipherAlg.equals( skeyAlg ) ) ) {
								            // DISCUSS: Should we thrown a ConfigurationException here or just log a warning??? I'm game for
								                //       either, but personally I'd prefer the squeaky wheel to the annoying throwing of
								                //       a ConfigurationException (which is a RuntimeException). Less likely to upset
								                //       the development community.
								                logger.warning(Logger.SECURITY_FAILURE, "Encryption mismatch between cipher algorithm (" +
											                    cipherAlg + ") and SecretKey algorithm (" + skeyAlg + "). Cipher will use algorithm " + cipherAlg);
								          }

							              byte[] ivBytes = null;
							              CipherSpec cipherSpec = new CipherSpec(encrypter, keySize);  // Could pass the ACTUAL (intended) key size
																            
																     // Using cipher mode that supports *both* confidentiality *and* authenticity? If so, then
																     // use the specified SecretKey as-is rather than computing a derived key from it. We also
																     // don't expect a separate MAC in the specified CipherText object so therefore don't try
																     // to validate it.
																     boolean preferredCipherMode = CryptoHelper.isCombinedCipherMode( cipherMode );
							       SecretKey encKey = null;
							       if ( preferredCipherMode ) {
								   encKey = key;
							       } else {
								   encKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(),
											      key, keySize, "encryption");
							       }
							              
								   if ( cipherSpec.requiresIV() ) {
								       String ivType = ESAPI.securityConfiguration().getIVType();
								       IvParameterSpec ivSpec = null;
								       if ( ivType.equalsIgnoreCase("random") ) {
									   ivBytes = ESAPI.randomizer().getRandomBytes(encrypter.getBlockSize());
									             } else if ( ivType.equalsIgnoreCase("fixed") ) {
									   String fixedIVAsHex = ESAPI.securityConfiguration().getFixedIV();
									   ivBytes = Hex.decode(fixedIVAsHex);
									   /* FUTURE     } else if ( ivType.equalsIgnoreCase("specified")) {
									   // FUTURE - TODO  - Create instance of specified class to use for IV generation and
									   //           use it to create the ivBytes. (The intent is to make sure that
									   //             1) IVs are never repeated for cipher modes like OFB and CFB, and
									   //           2) to screen for weak IVs for the particular cipher algorithm.
									   //    In meantime, use 'random' for block cipher in feedback mode. Unlikely they will
									   //    be repeated unless you are salting SecureRandom with same value each time. Anything
									   //    monotonically increasing should be suitable, like a counter, but need to remember
									                  //    it across JVM restarts. Was thinking of using System.currentTimeMillis(). While
									                  //    it's not perfect it probably is good enough. Could even all (advanced) developers
									                  //      to define their own class to create a unique IV to allow them some choice, but
									                  //      definitely need to provide a safe, default implementation.
									               */
									            } else {
									              // TODO: Update to add 'specified' once that is supported and added above.
									                  throw new ConfigurationException("Property Encryptor.ChooseIVMethod must be set to 'random' or 'fixed'");
									            }
								                ivSpec = new IvParameterSpec(ivBytes);
								                cipherSpec.setIV(ivBytes);
								                encrypter.init(Cipher.ENCRYPT_MODE, encKey, ivSpec);
								              } else {
								                encrypter.init(Cipher.ENCRYPT_MODE, encKey);
								              }
							              logger.debug(Logger.EVENT_SUCCESS, "Encrypting with " + cipherSpec);
							              byte[] raw = encrypter.doFinal(plaintext);
							       // Convert to CipherText.
							       CipherText ciphertext = new CipherText(cipherSpec, raw);
							              
								          // If we are using a "preferred" cipher mode--i.e., one that supports *both* confidentiality and
								          // authenticity, there is no point to store a separate MAC in the CipherText object. Thus we only
								   // do this when we are not using such a cipher mode.
								          if ( !preferredCipherMode ) {
								              // Compute derived key, and then use it to compute and store separate MAC in CipherText object.
								                  SecretKey authKey = computeDerivedKey(KeyDerivationFunction.kdfVersion, getDefaultPRF(),
															                                key, keySize, "authenticity");
								              ciphertext.computeAndStoreMAC(  authKey );
								          }
							              logger.debug(Logger.EVENT_SUCCESS, "JavaEncryptor.encrypt(SecretKey,byte[],boolean,boolean) -- success!");
							              success = true;  // W00t!!!
											        return ciphertext;
							           } catch (InvalidKeyException ike) {
							              throw new EncryptionException("Encryption failure: Invalid key exception.",
												               "Requested key size: " + keySize + "bits greater than 128 bits. Must install unlimited strength crypto extension from Sun: " +
												               ike.getMessage(), ike);
							            } catch (ConfigurationException cex) {
							              throw new EncryptionException("Encryption failure: Configuration error. Details in log.", "Key size mismatch or unsupported IV method. " +
												               "Check encryption key size vs. ESAPI.EncryptionKeyLength or Encryptor.ChooseIVMethod property.", cex);
							            } catch (InvalidAlgorithmParameterException e) {
							              throw new EncryptionException("Encryption failure (invalid IV)",
												               "Encryption problem: Invalid IV spec: " + e.getMessage(), e);
							            } catch (IllegalBlockSizeException e) {
							              throw new EncryptionException("Encryption failure (no padding used; invalid input size)",
												               "Encryption problem: Invalid input size without padding (" + xform + "). " + e.getMessage(), e);
							            } catch (BadPaddingException e) {
							              throw new EncryptionException("Encryption failure",
												               "[Note: Should NEVER happen in encryption mode.] Encryption problem: " + e.getMessage(), e);
							            } catch (NoSuchAlgorithmException e) {
							              throw new EncryptionException("Encryption failure (unavailable cipher requested)",
												               "Encryption problem: specified algorithm in cipher xform " + xform + " not available: " + e.getMessage(), e);
							            } catch (NoSuchPaddingException e) {
							              throw new EncryptionException("Encryption failure (unavailable padding scheme requested)",
												               "Encryption problem: specified padding scheme in cipher xform " + xform + " not available: " + e.getMessage(), e);
							            } finally {
							              // Don't overwrite anything in the case of exceptions because they may wish to retry.
								          if ( success && overwritePlaintext ) {
								            plain.overwrite();    // Note: Same as overwriting 'plaintext' byte array.
												      }
							         }
	   }

      /**
	     * Convenience method that decrypts previously encrypted plaintext strings
	     * that were encrypted using the new encryption mechanism (with CBC mode and
	     * PKCS5 padding by default).  This decryption method uses the master
	     * encryption key specified by the {@code Encryptor.MasterKey} property
	     * in {@code ESAPI.properties}.
	     *
	     * @param b64IVCiphertext  A base64-encoded representation of the
	     *               IV + raw ciphertext string to be decrypted with
	     *               the default master key.
	     * @return  The plaintext string prior to encryption.
	     * @throws EncryptionException When something fails with the decryption.
	     *
	     * @see org.owasp.esapi.Encryptor#decrypt(CipherText)
	     */
	   @Deprecated public String decrypt(String b64IVCiphertext) throws EncryptionException
									       {
	       logWarning("decrypt", "Calling deprecated decrypt() method.");
	     CipherText ct = null;
	     try {
	           // We assume that the default cipher transform was used to encrypt this.
		       ct = new CipherText();

	           // Need to base64 decode the IV+ciphertext and extract the IV to set it in CipherText object.
		       byte[] ivPlusRawCipherText = ESAPI.encoder().decodeFromBase64(b64IVCiphertext);
	           int blockSize = ct.getBlockSize();  // Size in bytes.
							        byte[] iv = new byte[ blockSize ];
	           CryptoHelper.copyByteArray(ivPlusRawCipherText, iv, blockSize);  // Copy the first blockSize bytes into iv array
										             int cipherTextSize = ivPlusRawCipherText.length - blockSize;
	           byte[] rawCipherText = new byte[ cipherTextSize ];
	           System.arraycopy(ivPlusRawCipherText, blockSize, rawCipherText, 0, cipherTextSize);
	           ct.setIVandCiphertext(iv, rawCipherText);

	           // Now the CipherText object should be prepared to use it to decrypt.
		       PlainText plaintext = decrypt(ct);
	           return plaintext.toString();  // Convert back to a Java String
						        } catch (UnsupportedEncodingException e) {
	           // Should never happen; UTF-8 should be in rt.jar.
		       logger.error(Logger.SECURITY_FAILURE, "UTF-8 encoding not available! Decryption failed.", e);
	           return null;  // CHECKME: Or re-throw or what? Could also use native encoding, but that's
				          // likely to cause unexpected and undesired effects far downstream.
				        } catch (IOException e) {
	           logger.error(Logger.SECURITY_FAILURE, "Base64 decoding of IV+ciphertext failed. Decryption failed.", e);
	           return null;
	         }
	   }

      /**
	   * {@inheritDoc}
	   */
	  public PlainText decrypt(CipherText ciphertext) throws EncryptionException {
	     // Now more of a convenience function for using the master key.
	         return decrypt(secretKeySpec, ciphertext);
	  }

      /**
	    * {@inheritDoc}
	    */
	  public PlainText decrypt(SecretKey key, CipherText ciphertext)
	      throws EncryptionException, IllegalArgumentException
		       {
	      long start = System.nanoTime();  // Current time in nanosecs; used to prevent timing attacks
	      if ( key == null ) {
	              throw new IllegalArgumentException("SecretKey arg may not be null");
	          }
	      if ( ciphertext == null ) {
	              throw new IllegalArgumentException("Ciphertext may arg not be null");
	          }

	      if ( ! CryptoHelper.isAllowedCipherMode(ciphertext.getCipherMode()) ) {
	              // This really should be an illegal argument exception, but it could
		          // mean that a partner encrypted something using a cipher mode that
		          // you do not accept, so it's a bit more complex than that. Also
		          // throwing an IllegalArgumentException doesn't allow us to provide
		          // the two separate error messages or automatically log it.
		          throw new EncryptionException(DECRYPTION_FAILED,
							                  "Invalid cipher mode " + ciphertext.getCipherMode() +
							          " not permitted for decryption or encryption operations.");
	          }
	      logger.debug(Logger.EVENT_SUCCESS,
			                 "Args valid for JavaEncryptor.decrypt(SecretKey,CipherText): " +
			                 ciphertext);

	      PlainText plaintext = null;
	      boolean caughtException = false;
	      int progressMark = 0;
	      try {
	              // First we validate the MAC.
		          boolean valid = CryptoHelper.isCipherTextMACvalid(key, ciphertext);
	              if ( !valid ) {
		              try {
		                      // This is going to fail, but we want the same processing
			                  // to occur as much as possible so as to prevent timing
			                  // attacks. We _could_ just be satisfied by the additional
			                  // sleep in the 'finally' clause, but an attacker on the
			                  // same server who can run something like 'ps' can tell
			                  // CPU time versus when the process is sleeping. Hence we
			                  // try to make this as close as possible. Since we know
			                  // it is going to fail, we ignore the result and ignore
			                  // the (expected) exception.
			                  handleDecryption(key, ciphertext); // Ignore return (should fail).
		                  } catch(Exception ex) {
		                      ;   // Ignore
		                  }
		              throw new EncryptionException(DECRYPTION_FAILED,
							                          "Decryption failed because MAC invalid for " +
							                          ciphertext);
		          }
	              progressMark++;
	              // The decryption only counts if the MAC was valid.
		          plaintext = handleDecryption(key, ciphertext);
	              progressMark++;
	          } catch(EncryptionException ex) {
	              caughtException = true;
	              String logMsg = null;
	              switch( progressMark ) {
		          case 1:
		              logMsg = "Decryption failed because MAC invalid. See logged exception for details.";
		              break;
		          case 2:
		              logMsg = "Decryption failed because handleDecryption() failed. See logged exception for details.";
		              break;
		          default:
		              logMsg = "Programming error: unexpected progress mark == " + progressMark;
		          break;
		          }
	              logger.error(Logger.SECURITY_FAILURE, logMsg);
	              throw ex;           // Re-throw
	          }
	      finally {
	              if ( caughtException ) {
		              // The rest of this code is to try to account for any minute differences
		                  // in the time it might take for the various reasons that decryption fails
		                  // in order to prevent any other possible timing attacks. Perhaps it is
		                  // going overboard. If nothing else, if N_SECS is large enough, it might
		                  // deter attempted repeated attacks by making them take much longer.
		                  long now = System.nanoTime();
		              long elapsed = now - start;
		              final long NANOSECS_IN_SEC = 1000000000L; // nanosec is 10**-9 sec
		              long nSecs = N_SECS * NANOSECS_IN_SEC;  // N seconds in nano seconds
		              if ( elapsed < nSecs ) {
		                      // Want to sleep so total time taken is N seconds.
			                  long extraSleep = nSecs - elapsed;

		                      // 'extraSleep' is in nanoseconds. Need to convert to a millisec
			                  // part and nanosec part. Nanosec is 10**-9, millsec is
			                  // 10**-3, so divide by (10**-9 / 10**-3), or 10**6 to
			                  // convert to from nanoseconds to milliseconds.
			                  long millis = extraSleep / 1000000L;
		                      long nanos  = (extraSleep - (millis * 1000000L));
		                      assert nanos >= 0 && nanos <= Integer.MAX_VALUE :
		    "Nanosecs out of bounds; nanos = " + nanos;
		                      try {
			                      Thread.sleep(millis, (int)nanos);
			                  } catch(InterruptedException ex) {
			                      ;   // Ignore
			                  }
		                  } // Else ... time already exceeds N_SECS sec, so do not sleep.
		          }
	          }
	      return plaintext;
	  }

    // Handle the actual decryption portion. At this point it is assumed that
    // any MAC has already been validated. (But see "DISCUSS" issue, below.)
    private PlainText handleDecryption(SecretKey key, CipherText ciphertext)
	throws EncryptionException
    {
	int keySize = 0;
	try {
	    Cipher decrypter = Cipher.getInstance(ciphertext.getCipherTransformation());
	    keySize = key.getEncoded().length * 8;  // Convert to # bits

	    // Using cipher mode that supports *both* confidentiality *and* authenticity? If so, then
	    // use the specified SecretKey as-is rather than computing a derived key from it. We also
	    // don't expect a separate MAC in the specified CipherText object so therefore don't try
	    // to validate it.
	    boolean preferredCipherMode = CryptoHelper.isCombinedCipherMode( ciphertext.getCipherMode() );
	    SecretKey encKey = null;
	    if ( preferredCipherMode ) {
		encKey = key;
	    } else {
		// TODO: PERFORMANCE: Calculate avg time this takes and consider caching for very short interval
		//       (e.g., 2 to 5 sec tops). Otherwise doing lots of encryptions in a loop could take a LOT longer.
		//       But remember Jon Bentley's "Rule #1 on performance: First make it right, then make it fast."
		  //     This would be a security trade-off as it would leave keys in memory a bit longer, so it
		      //     should probably be off by default and controlled via a property.
		    encKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(),
						                        key, keySize, "encryption");
	    }
	    if ( ciphertext.requiresIV() ) {
		decrypter.init(Cipher.DECRYPT_MODE, encKey, new IvParameterSpec(ciphertext.getIV()));
	    } else {
		decrypter.init(Cipher.DECRYPT_MODE, encKey);
	    }
	    byte[] output = decrypter.doFinal(ciphertext.getRawCipherText());
	    return new PlainText(output);

	} catch (InvalidKeyException ike) {
	    throw new EncryptionException(DECRYPTION_FAILED, "Must install JCE Unlimited Strength Jurisdiction Policy Files from Sun", ike);
	} catch (NoSuchAlgorithmException e) {
	    throw new EncryptionException(DECRYPTION_FAILED, "Invalid algorithm for available JCE providers - " +
					  ciphertext.getCipherTransformation() + ": " + e.getMessage(), e);
	} catch (NoSuchPaddingException e) {
	    throw new EncryptionException(DECRYPTION_FAILED, "Invalid padding scheme (" +
					  ciphertext.getPaddingScheme() + ") for cipher transformation " + ciphertext.getCipherTransformation() +
					  ": " + e.getMessage(), e);
	} catch (InvalidAlgorithmParameterException e) {
	    throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e);
	} catch (IllegalBlockSizeException e) {
	    throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e);
	} catch (BadPaddingException e) {
	    //DISCUSS: This needs fixed. Already validated MAC in CryptoHelper.isCipherTextMACvalid() above.
	    //So only way we could get a padding exception is if invalid padding were used originally by
	    //the party doing the encryption. (This might happen with a buggy padding scheme for instance.)
	    //It *seems* harmless though, so will leave it for now, and technically, we need to either catch it
	    //or declare it in a throws class. Clearly we don't want to do the later. This should be discussed
	    //during a code inspection.
	    SecretKey authKey;
	    try {
		authKey = computeDerivedKey( ciphertext.getKDFVersion(), ciphertext.getKDF_PRF(),
					                              key, keySize, "authenticity");
	    } catch (Exception e1) {
		throw new EncryptionException(DECRYPTION_FAILED,
					      "Decryption problem -- failed to compute derived key for authenticity: " + e1.getMessage(), e1);
	    }
	    boolean success = ciphertext.validateMAC( authKey );
	    if ( success ) {
		throw new EncryptionException(DECRYPTION_FAILED, "Decryption problem: " + e.getMessage(), e);
	    } else {
		throw new EncryptionException(DECRYPTION_FAILED,
					      "Decryption problem: WARNING: Adversary may have tampered with " +
					      "CipherText object orCipherText object mangled in transit: " + e.getMessage(), e);
	    }
	}
    }
      
	  /**
	       * {@inheritDoc}
	       */
	  public String sign(String data) throws EncryptionException {
	    try {
	          Signature signer = Signature.getInstance(signatureAlgorithm);
	          signer.initSign(privateKey);
	          signer.update(data.getBytes(encoding));
	          byte[] bytes = signer.sign();
	          return ESAPI.encoder().encodeForBase64(bytes, false);
	        } catch (InvalidKeyException ike) {
	          throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike);
	        } catch (Exception e) {
	          throw new EncryptionException("Signature failure", "Can't find signature algorithm " + signatureAlgorithm, e);
	        }
	  }
        
	  /**
	       * {@inheritDoc}
	       */
	  public boolean verifySignature(String signature, String data) {
	    try {
	          byte[] bytes = ESAPI.encoder().decodeFromBase64(signature);
	          Signature signer = Signature.getInstance(signatureAlgorithm);
	          signer.initVerify(publicKey);
	          signer.update(data.getBytes(encoding));
	          return signer.verify(bytes);
	        } catch (Exception e) {
	            // NOTE: EncryptionException constructed *only* for side-effect of causing logging.
		        // FindBugs complains about this and since it examines byte-code, there's no way to
		        // shut it up.
		      new EncryptionException("Invalid signature", "Problem verifying signature: " + e.getMessage(), e);
	          return false;
	        }
	  }

      /**
	   * {@inheritDoc}
	 *
	 * @param expiration
	 * @throws IntegrityException
	 */
	  public String seal(String data, long expiration) throws IntegrityException {
	      if ( data == null ) {
	              throw new IllegalArgumentException("Data to be sealed may not be null.");
	          }
	      
	        try {
	            String b64data = null;
	    try {
		b64data = ESAPI.encoder().encodeForBase64(data.getBytes("UTF-8"), false);
	    } catch (UnsupportedEncodingException e) {
		; // Ignore; should never happen since UTF-8 built into rt.jar
	    }
	          // mix in some random data so even identical data and timestamp produces different seals
		      String nonce = ESAPI.randomizer().getRandomString(10, EncoderConstants.CHAR_ALPHANUMERICS);
	          String plaintext = expiration + ":" + nonce + ":" + b64data;
	          // add integrity check; signature is already base64 encoded.
		      String sig = this.sign( plaintext );
	          CipherText ciphertext = this.encrypt( new PlainText(plaintext + ":" + sig) );
	          String sealedData = ESAPI.encoder().encodeForBase64(ciphertext.asPortableSerializedByteArray(), false);
	          return sealedData;
	        } catch( EncryptionException e ) {
	          throw new IntegrityException( e.getUserMessage(), e.getLogMessage(), e );
	        }
	  }

      /**
	   * {@inheritDoc}
	   */
	  public String unseal(String seal) throws EncryptionException {
	    PlainText plaintext = null;
	    try {
	            byte[] encryptedBytes = ESAPI.encoder().decodeFromBase64(seal);
	            CipherText cipherText = null;
	            try {
		            cipherText = CipherText.fromPortableSerializedBytes(encryptedBytes);
		        } catch( AssertionError e) {
		              // Some of the tests in EncryptorTest.testVerifySeal() are examples of
		                // this if assertions are enabled.
		                throw new EncryptionException("Invalid seal",
							                                                  "Seal passed garbarge data resulting in AssertionError: " + e);
		          }
	          plaintext = this.decrypt(cipherText);

	          String[] parts = plaintext.toString().split(":");
	          if (parts.length != 4) {
		        throw new EncryptionException("Invalid seal", "Seal was not formatted properly.");
		      }
	      
		      String timestring = parts[0];
	          long now = new Date().getTime();
	          long expiration = Long.parseLong(timestring);
	          if (now > expiration) {
		        throw new EncryptionException("Invalid seal", "Seal expiration date of " + new Date(expiration) + " has past.");
		      }
	          String nonce = parts[1];
	          String b64data = parts[2];
	          String sig = parts[3];
	          if (!this.verifySignature(sig, timestring + ":" + nonce + ":" + b64data ) ) {
		        throw new EncryptionException("Invalid seal", "Seal integrity check failed");
		      }  
			         return new String(ESAPI.encoder().decodeFromBase64(b64data), "UTF-8");
	        } catch (EncryptionException e) {
	          throw e;
	        } catch (Exception e) {
	          throw new EncryptionException("Invalid seal", "Invalid seal:" + e.getMessage(), e);
	        }
	  }

      
	  /**
	       * {@inheritDoc}
	       */
	  public boolean verifySeal( String seal ) {
	    try {
	          unseal( seal );
	          return true;
	        } catch( EncryptionException e ) {
	          return false;
	        }
	  }
      
	  /**
	       * {@inheritDoc}
	       */
	  public long getTimeStamp() {
	    return new Date().getTime();
	  }

      /**
	   * {@inheritDoc}
	   */
	  public long getRelativeTimeStamp( long offset ) {
	    return new Date().getTime() + offset;
	  }

      // DISCUSS: Why experimental? Would have to be added to Encryptor interface
	  //      but only 3 things I saw wrong with this was 1) it used HMacMD5 instead
	  //      of HMacSHA1 (see discussion below), 2) that the HMac key is the
	  //      same one used for encryption (also see comments), and 3) it caught
	  //      overly broad exceptions. Here it is with these specific areas
	  //      addressed, but no unit testing has been done at this point. -kww
    /**
     * Compute an HMAC for a String.  Experimental.
     * @param input  The input for which to compute the HMac.
     */
    /********************
   public String computeHMAC( String input ) throws EncryptionException {
     try {
       Mac hmac = Mac.getInstance("HMacSHA1"); // DISCUSS: Changed to HMacSHA1. MD5 *badly* broken
                            //          SHA1 should really be avoided, but using
                            //       for HMAC-SHA1 is acceptable for now. Plan
                            //       to migrate to SHA-256 or NIST replacement for
                            //       SHA1 in not too distant future.
       // DISCUSS: Also not recommended that the HMac key is the same as the one
       //      used for encryption (namely, Encryptor.MasterKey). If anything it
       //      would be better to use Encryptor.MasterSalt for the HMac key, or
       //      perhaps a derived key based on the master salt. (One could use
       //      KeyDerivationFunction.computeDerivedKey().)
       //
       byte[] salt = ESAPI.securityConfiguration().getMasterSalt();
       hmac.init( new SecretKeySpec(salt, "HMacSHA1") );  // Was:  hmac.init(secretKeySpec)  
       byte[] inBytes;
       try {
         inBytes = input.getBytes("UTF-8");
       } catch (UnsupportedEncodingException e) {
         logger.warning(Logger.SECURITY_FAILURE, "computeHMAC(): Can't find UTF-8 encoding; using default encoding", e);
         inBytes = input.getBytes();
       }
       byte[] bytes = hmac.doFinal( inBytes );
       return ESAPI.encoder().encodeForBase64(bytes, false);
     } catch (InvalidKeyException ike) {
       throw new EncryptionException("Encryption failure", "Must install unlimited strength crypto extension from Sun", ike);
       } catch (NoSuchAlgorithmException e) {
         throw new EncryptionException("Could not compute HMAC", "Can't find HMacSHA1 algorithm. " +
                                     "Problem computing HMAC for " + input, e );
       }
   }
    ********************/

    /**
     * Log a security warning every Nth time one of the deprecated encrypt or
     * decrypt methods are called. ('N' is hard-coded to be 25 by default, but
     * may be changed via the system property
     * {@code ESAPI.Encryptor.warnEveryNthUse}.) In other words, we nag
     * them until the give in and change it. ;-)
     *
     * @param where The string "encrypt" or "decrypt", corresponding to the
     *              method that is being logged.
     * @param msg   The message to log.
     */
	private void logWarning(String where, String msg) {
	int counter = 0;
	if ( where.equals("encrypt") ) {
	    counter = encryptCounter++;
	    where = "JavaEncryptor.encrypt(): [count=" + counter +"]";
	} else if ( where.equals("decrypt") ) {
	    counter = decryptCounter++;
	    where = "JavaEncryptor.decrypt(): [count=" + counter +"]";
	} else {
	    where = "JavaEncryptor: Unknown method: ";
	}
	// We log the very first time (note the use of post-increment on the
	// counters) and then every Nth time thereafter. Logging every single
	// time is likely to be way too much logging.
	if ( (counter % logEveryNthUse) == 0 ) {
	    logger.warning(Logger.SECURITY_FAILURE, where + msg);
	}
    }

    private KeyDerivationFunction.PRF_ALGORITHMS getPRF(String name) {      
	        String prfName = null;
	    if ( name == null ) {
	          prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
	        } else {
	          prfName = name;
	        }
	    KeyDerivationFunction.PRF_ALGORITHMS prf = KeyDerivationFunction.convertNameToPRF(prfName);
	    return prf;
    }

    private KeyDerivationFunction.PRF_ALGORITHMS getDefaultPRF() {
	    String prfName = ESAPI.securityConfiguration().getKDFPseudoRandomFunction();
	    return getPRF(prfName);
    }

    // Private interface to call ESAPI's KDF to get key for encryption or authenticity.
    private SecretKey computeDerivedKey(int kdfVersion, KeyDerivationFunction.PRF_ALGORITHMS prf,
					                  SecretKey kdk, int keySize, String purpose)
	  throws NoSuchAlgorithmException, InvalidKeyException, EncryptionException
    {
	  // These really should be turned into actual runtime checks and an
	      // IllegalArgumentException should be thrown if they are violated.
	      // But this should be OK since this is a private method. Also, this method will
	      // be called quite often so assertions are a big win as they can be disabled or
	      // enabled at will.
	      assert prf != null : "Pseudo Random Function for KDF cannot be null";
	  assert kdk != null : "Key derivation key cannot be null.";
	  // We would choose a larger minimum key size, but we want to be
	      // able to accept DES for legacy encryption needs. NIST says 112-bits is min. If less than that,
	      // we print warning.
	      assert keySize >= 56 : "Key has size of " + keySize + ", which is less than minimum of 56-bits.";
	  assert (keySize % 8) == 0 : "Key size (" + keySize + ") must be a even multiple of 8-bits.";
	  assert purpose != null : "Purpose cannot be null. Should be 'encryption' or 'authenticity'.";
	  assert purpose.equals("encryption") || purpose.equals("authenticity") :
	    "Purpose must be \"encryption\" or \"authenticity\".";

	  KeyDerivationFunction kdf = new KeyDerivationFunction(prf);
	  if ( kdfVersion != 0 ) {
	        kdf.setVersion(kdfVersion);
	      }
	  return kdf.computeDerivedKey(kdk, keySize, purpose);
    }

    // Get all the algorithms we will be using from ESAPI.properties.
    private static void setupAlgorithms() {
	// setup algorithms
	encryptAlgorithm = ESAPI.securityConfiguration().getEncryptionAlgorithm();
	signatureAlgorithm = ESAPI.securityConfiguration().getDigitalSignatureAlgorithm();
	randomAlgorithm = ESAPI.securityConfiguration().getRandomAlgorithm();
	hashAlgorithm = ESAPI.securityConfiguration().getHashAlgorithm();
	hashIterations = ESAPI.securityConfiguration().getHashIterations();
	encoding = ESAPI.securityConfiguration().getCharacterEncoding();
	encryptionKeyLength = ESAPI.securityConfiguration().getEncryptionKeyLength();
	signatureKeyLength = ESAPI.securityConfiguration().getDigitalSignatureKeyLength();
    }

    // Set up signing key pair using the master password and salt. Called (once)
    // from the JavaEncryptor CTOR.
    private static void initKeyPair(SecureRandom prng) throws NoSuchAlgorithmException {
	String sigAlg = signatureAlgorithm.toLowerCase();
	if ( sigAlg.endsWith("withdsa") ) {
	    //
	    // Admittedly, this is a kludge. However for Sun JCE, even though
	    // "SHA1withDSA" is a valid signature algorithm name, if one calls
	    //      KeyPairGenerator kpg = KeyPairGenerator.getInstance("SHA1withDSA");
	    // that will throw a NoSuchAlgorithmException with an exception
	    // message of "SHA1withDSA KeyPairGenerator not available". Since
	    // SHA1withDSA and DSA keys should be identical, we use "DSA"
	    // in the case that SHA1withDSA or SHAwithDSA was specified. This is
	    // all just to make these 2 work as expected. Sigh. (Note:
	    // this was tested with JDK 1.6.0_21, but likely fails with earlier
	    // versions of the JDK as well.)
	    //
	    sigAlg = "DSA";
	} else if ( sigAlg.endsWith("withrsa") ) {
	    // Ditto for RSA.
	    sigAlg = "RSA";
	}
	KeyPairGenerator keyGen = KeyPairGenerator.getInstance(sigAlg);
	keyGen.initialize(signatureKeyLength, prng);
	KeyPair pair = keyGen.generateKeyPair();
	privateKey = pair.getPrivate();
	publicKey = pair.getPublic();
    }
}


[-- Attachment #3: efbbbf-strip.cpp --]
[-- Type: text/x-c++src, Size: 2971 bytes --]

// g++ -g -O2 efbbbf-strip.cpp -o efbbbf-strip.exe

#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
using std::ios_base;

#include <fstream>
using std::ifstream;
using std::ofstream;

#include <string>
using std::string;

#include <cstring>
using std::strlen;

static const string EFBBBF = string("\xef\xbb\xbf");
static const unsigned int EFBBBF_LEN = 3;

bool Strip(const char* filename);

int main(int argc, char* argv[])
{
  if(argc == 1)
  {
    cerr << "Strips text files of UTF-8 byte order marks (BOM) in place." << endl;
    cerr << "  Usage: efbbbf-strip.exe <text file> [<text file> …]" << endl;
    return 1;
  }

  for(unsigned int i = 1; i < (unsigned int)argc; i++)
  {
    Strip(argv[i]);
  }
}

bool Strip(const char* filename)
{
  if ( !filename || 0 == strlen(filename) )
    return false;

  //////////////////////////////////////////////////////////
  // Read the file
  //////////////////////////////////////////////////////////

  ifstream ifile(filename);
  if( !ifile.good() )
  {
    cerr << "Unable to open " << filename << " for reading" << endl;
    return false;
  }
  
  // Reserve size to save on allocations
  ifile.seekg(0, ios_base::end);
  if( !ifile.good() )
  {
    cerr << "Failed to seek to end of " << filename << endl;
    return false;
  }

  size_t fsize = ifile.tellg();
  if( !ifile.good() )
  {
    cerr << "Failed to determine size of " << filename << endl;
    return false;
  }

  ifile.seekg(0, ios_base::beg);
  if( !ifile.good() )
  {
    cerr << "Failed to seek to beginning of " << filename << endl;
    return false;
  }

  string str, line;
  str.reserve(fsize);

  // Read the file line by line. Not sure why reading in one fell swoop failed....
  while(std::getline(ifile, line))
  {
    str += line;
    str += "\n";

    if( !ifile.good() )
      break;
  }

  ifile.close();

  //////////////////////////////////////////////////////////
  // Fix the file
  //////////////////////////////////////////////////////////

  size_t count = 0;
  string::size_type pos = 1;
  while( (pos = str.find(EFBBBF, pos)) != string::npos )
  {
     str.replace(pos, EFBBBF_LEN, "");
     count++;
  }

  if(0 == count)
  {
    cout << filename << ": no replacements" << endl;
    return true;
  }

  //////////////////////////////////////////////////////////
  // Write the file
  //////////////////////////////////////////////////////////

  ofstream ofile(filename, ios_base::out | ios_base::trunc);
  if( !ofile.good() )
  {
    cerr << "Unable to open " << filename << " for writing" << endl;
    return false;
  }

  ofile.write(str.data(), str.size());
  if( !ofile.good() )
  {
    cerr << "Failed to write " << filename << endl;
    return false;
  }

  ofile.close();

  //////////////////////////////////////////////////////////
  // Book keeping
  //////////////////////////////////////////////////////////

  cout << filename << ": " << count << " replacements" << endl;
  return true;
}


^ permalink raw reply	[flat|nested] 3+ messages in thread

* bug#9747: C-x h TAB and M-x untabify
  2011-10-13 23:27 bug#9747: C-x h TAB and M-x untabify Jeffrey Walton
@ 2011-10-19 23:32 ` Juri Linkov
  2021-07-16 13:57   ` bug#9747: M-x untabify with "ZERO WIDTH NO-BREAK SPACE" (aka "BYTE ORDER MARK") Lars Ingebrigtsen
  0 siblings, 1 reply; 3+ messages in thread
From: Juri Linkov @ 2011-10-19 23:32 UTC (permalink / raw)
  To: noloader; +Cc: 9747

> I often use C-x h TAB and M-x untabify to format C, C++, and Java code.
>
> If a document has an errant UTF-8 byte order mark (a UTF-8 BOM is EF
> BB BF), Emacs cannot always format the source file.
>
> For example, the attached Java file (JavaEncryptor.java-backup) has
> 1845 BOMs sprinkled throughout. I'm not sure what editor put them in,
> but Emacs does not properly handle some operations with them present.
> If I strip the errant BOMs with the attached program
> (efbbbf-strip.cpp), Emacs will properly format the file.

"BYTE ORDER MARK" is the old name of the U+FEFF character.
The new name is "ZERO WIDTH NO-BREAK SPACE".

You can add to your .emacs something like:

(eval-after-load "cc-mode"
  '(progn (modify-syntax-entry ?\uFEFF " " java-mode-syntax-table)))

and the most of indentation code will work correctly.

However, in some places in core packages we need to replace such code

  (skip-chars-forward " \t")

with

  (skip-chars-forward " \t\uFEFF")

to take into account other whitespace characters.





^ permalink raw reply	[flat|nested] 3+ messages in thread

* bug#9747: M-x untabify with "ZERO WIDTH NO-BREAK SPACE" (aka "BYTE ORDER MARK")
  2011-10-19 23:32 ` Juri Linkov
@ 2021-07-16 13:57   ` Lars Ingebrigtsen
  0 siblings, 0 replies; 3+ messages in thread
From: Lars Ingebrigtsen @ 2021-07-16 13:57 UTC (permalink / raw)
  To: Juri Linkov; +Cc: noloader, 9747

Juri Linkov <juri@jurta.org> writes:

>> I often use C-x h TAB and M-x untabify to format C, C++, and Java code.
>>
>> If a document has an errant UTF-8 byte order mark (a UTF-8 BOM is EF
>> BB BF), Emacs cannot always format the source file.
>>
>> For example, the attached Java file (JavaEncryptor.java-backup) has
>> 1845 BOMs sprinkled throughout. I'm not sure what editor put them in,
>> but Emacs does not properly handle some operations with them present.
>> If I strip the errant BOMs with the attached program
>> (efbbbf-strip.cpp), Emacs will properly format the file.
>
> "BYTE ORDER MARK" is the old name of the U+FEFF character.
> The new name is "ZERO WIDTH NO-BREAK SPACE".

So I don't think there's anything here to fix on the Emacs side --
zero-width spaces aren't necessarily supposed to be handled identically
to other white space here.  So I'm closing this bug report.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no





^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-07-16 13:57 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-10-13 23:27 bug#9747: C-x h TAB and M-x untabify Jeffrey Walton
2011-10-19 23:32 ` Juri Linkov
2021-07-16 13:57   ` bug#9747: M-x untabify with "ZERO WIDTH NO-BREAK SPACE" (aka "BYTE ORDER MARK") Lars Ingebrigtsen

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).