//============================================================================== // CipherSpi.java //============================================================================== package tribble.crypto; // System imports import java.lang.Exception; import java.lang.NullPointerException; import java.lang.String; import java.lang.System; import java.lang.UnsupportedOperationException; import java.security.AlgorithmParameters; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; /******************************************************************************* * Cryptographic cipher service provider interface (SPI). * *
* This is the base class for all encryption cipher SPI implementations within * this package. Cipher objects (of type javax.crypto.Cipher) are * constructed from a combination of a cipher SPI object, which is one of generic * cipher algorithm types derived from this class, {@link CipherSpi}, and a * primitive cipher algorithm, which is a class derived from class {@link * AbstractCipher}. * *
* Cipher SPI implementations derived from this class are further divided into * two types, {@link BlockCipherSpi} and {@link StreamCipherSpi}. These two base * classes implement the encryption cipher service provider interfaces that serve * as the foundation upon which the rest of this cryptographic package is built. * *
* The {@link #MODE_ECB MODE_XXX} constants define the various modes * that can be supported by the cryptographic cipher classes in this package. * Likewise, the {@link #PADDING_NONE PADDING_XXX} constants define the * various padding schemes for this package. * * * @version API 2, $Revision: 1.6 $ $Date: 2005/09/10 23:01:47 $ * @since JCE 1.2 / JRE 1.4, 2003-04-04 * @author * David R. Tribble * (david@tribble.com). * *
* Copyright ©2003-2005 by David R. Tribble, all rights reserved.
*
* Permission is granted to freely use and distribute this source code
* provided that the original copyright and authorship notices remain
* intact.
*
* @see BlockCipherSpi
* @see StreamCipherSpi
* @see AbstractCipher
*/
public abstract class CipherSpi
extends javax.crypto.CipherSpi
{
// Identification
/** Revision information. */
static final String REV =
"@(#)tribble/crypto/CipherSpi.java $Revision: 1.6 $ $Date: 2005/09/10 23:01:47 $\n";
/** Class series version. */
public static final int SERIES = 200;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Constants
//----------------------------------
// Cipher initialization modes
/** Initialization mode: Encryption.
* (Same as Cipher.ENCRYPT_MODE.)
*/
public static final short ENCRYPT_MODE = Cipher.ENCRYPT_MODE;
/** Initialization mode: Decryption.
* (Same as Cipher.DECRYPT_MODE.)
*/
public static final short DECRYPT_MODE = Cipher.DECRYPT_MODE;
//----------------------------------
// Operating mode types
/** Operating mode (block): ECB, electronic codebook (no chaining). */
public static final String MODE_ECB = "ECB";
/** Operating mode (block): CBC, cipher block chaining. */
public static final String MODE_CBC = "CBC";
/** Operating mode (block): PBC, plaintext block chaining. */
public static final String MODE_PBC = "PBC";
/** Operating mode (block): PCBC, propagating cipher block chaining. */
public static final String MODE_PCBC = "PCBC";
/** Operating mode (block): BC, block chaining. */
public static final String MODE_BC = "BC";
/** Operating mode (stream): CFB, cipher feedback. */
public static final String MODE_CFB = "CFB";
/** Operating mode (stream): CFB-8, cipher feedback. */
public static final String MODE_CFB8 = "CFB8";
/** Operating mode (stream): OFB, output feedback. */
public static final String MODE_OFB = "OFB";
/** Operating mode (stream): OFB-8, output feedback. */
public static final String MODE_OFB8 = "OFB8";
/** Operating mode (stream): PFB, plaintext feedback. */
public static final String MODE_PFB = "PFB";
/** Operating mode (stream): PFB-8, plaintext feedback. */
public static final String MODE_PFB8 = "PFB8";
/** Operating mode (stream): CTR, counter mode. */
public static final String MODE_CTR = "CTR";
/** Operating mode (stream): CTR-8, counter mode. */
public static final String MODE_CTR8 = "CTR8";
//----------------------------------
// Padding scheme types
/** Padding scheme: None. */
public static final String PADDING_NONE = "NoPadding";
/** Padding scheme: PKCS#5. */
public static final String PADDING_PKCS5 = "PKCS5Padding";
/** Padding scheme: Pad with zero bytes (nulls). */
public static final String PADDING_ZEROS = "NullPadding";
/** Padding scheme: Ciphertext stealing in the final partial block. */
public static final String PADDING_CTS = "CiphertextStealing";
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Static variables
/** Default source of random values. */
static volatile SecureRandom s_rand;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Variables
/** Cipher algorithm name. */
String m_alg;
/** Padding scheme. */
String m_padding = PADDING_NONE;
/** Operating mode. */
String m_mode = MODE_ECB;
/** Source of random values. */
SecureRandom m_rand;
/** Initial vector (IV). */
byte[] m_iv;
/** Incomplete (partial) input block. */
byte[] m_in;
/** Block size (in bytes). */
int m_blockLen;
/** Key size (in bytes). */
int m_keyLen;
/** Number of bytes currently pending in {@link #m_in}. */
int m_inSize;
/** Encrypt/decrypt operation. */
boolean m_decrypt;
/** Cipher has been initialized. */
boolean m_init;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Constructors
/***************************************************************************
* Constructor.
*
* @param alg
* Cryptographic cipher algorithm name.
*
* @param mode
* Operating mode, which must match one of the
* {@link #MODE_CFB MODE_XXX} constants.
*
* @param pad
* Padding scheme, which must match one of the
* {@link #PADDING_NONE PADDING_XXX} constants}.
*
* @throws NoSuchAlgorithmException
* Thrown if mode does not specify a supported operating mode.
*
* @throws NoSuchPaddingException
* Thrown if pad does not specify a supported padding scheme.
*
* @since 1.5 (JCE 1.2), 2005-07-10
*/
protected CipherSpi(String alg, String mode, String pad)
throws NoSuchAlgorithmException, NoSuchPaddingException
{
// Initialize
m_alg = alg;
engineSetMode(mode);
engineSetPadding(pad);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Methods
/***************************************************************************
* Initialize this cipher.
*
* @param mode
* Ciphering mode, which is either {@link #ENCRYPT_MODE} or
* {@link #DECRYPT_MODE}.
*
* @param key
* Key for this cipher to use.
*
* @param rand
* Source of random values, or null if they are to be supplied by a default
* source.
*
* @throws InvalidKeyException
* Thrown if key is not the proper kind of key for use with this
* cipher.
*
* @throws IllegalArgumentException (unchecked)
* Thrown if mode is not a valid value.
*
* @since 1.1 (JCE 1.2), 2003-03-30
*/
protected abstract void engineInit(int mode, Key key, SecureRandom rand)
throws InvalidKeyException;
//overrides javax.crypto.CipherSpi
/***************************************************************************
* Initialize this cipher.
*
* @param mode
* Ciphering mode, which is either {@link #ENCRYPT_MODE} or
* {@link #DECRYPT_MODE}.
*
* @param key
* Key for this cipher to use.
*
* @param parms
* Algorithm-specific initialization parameters (e.g., an IV passed as a
* javax.crypto.spec.IvParameterSpec object).
*
* @param rand
* Source of random values, or null if this is to be supplied by a default
* source.
*
* @throws InvalidKeyException
* Thrown if key is not the proper kind of key for use with this
* cipher.
*
* @throws InvalidAlgorithmParameterException
* Thrown if parms is not a proper initialization parameter for this
* cipher.
*
* @since 1.1 (JCE 1.2), 2003-03-30
*/
protected abstract void engineInit(int mode, Key key,
AlgorithmParameterSpec parms, SecureRandom rand)
throws InvalidKeyException, InvalidAlgorithmParameterException;
//overrides javax.crypto.CipherSpi
/***************************************************************************
* Initialize this cipher.
*
* @param mode
* Ciphering mode, which is either {@link #ENCRYPT_MODE} or
* {@link #DECRYPT_MODE}.
*
* @param key
* Key for this cipher to use.
*
* @param parms
* Algorithm-specific initialization parameters (e.g., an IV passed as a
* javax.crypto.spec.IvParameterSpec object).
*
* @param rand
* Source of random values, or null if this is to be supplied by a default
* source.
*
* @throws InvalidKeyException
* Thrown if key is not the proper kind of key for use with this
* cipher.
*
* @throws InvalidAlgorithmParameterException
* Thrown if parms is not a proper initialization parameter for this
* cipher.
*
* @since 1.1 (JCE 1.2), 2003-03-30
*/
protected abstract void engineInit(int mode, Key key,
AlgorithmParameters parms, SecureRandom rand)
throws InvalidKeyException, InvalidAlgorithmParameterException;
//overrides javax.crypto.CipherSpi
/***************************************************************************
* Set the operating mode of this cipher.
*
*
* Subclasses must override this method, and may call * {@link #engineSetMode(String, String[]) engineSetMode()}. * * * @param mode * Cipher operating mode to use * (e.g., "ECB", "CBC", "CFB", "OFB", * "PCBC", etc.). * See the {@link #MODE_ECB MODE_XXX} constants. * * @throws NoSuchAlgorithmException * Thrown if mode does not specify an operating mode supported by * this cipher. * * @throws NullPointerException (unchecked) * Thrown if mode is null. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected abstract void engineSetMode(String mode) throws NoSuchAlgorithmException; //overrides javax.crypto.CipherSpi /*************************************************************************** * Set the padding scheme of this cipher. * * @param padding * Cipher padding scheme to use (e.g., "PKCS5Padding"). * * @throws NoSuchPaddingException * Thrown if padding does not specify a padding scheme supported by * this cipher. * * @throws NullPointerException (unchecked) * Thrown if padding is null. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected abstract void engineSetPadding(String padding) throws NoSuchPaddingException; //overrides javax.crypto.CipherSpi /*************************************************************************** * Set the initial vector (IV) used by this cipher. * *
* This method is not part of the standard set defined in class * javax.crypto.CipherSpi, but is provided as a convenient * enhancement. * * * @param iv * Initial vector bytes to be used by this cipher. * This array must be no longer than the cipher block size * (see {@link #engineGetBlockSize}). * It can be shorter than the cipher block size, in which case the IV of this * cipher will be padded on the right with zeros. * Note that the contents of this array are copied to an internal buffer, so * that subsequent changes made to the array do not affect this cipher * object. * * @throws InvalidAlgorithmParameterException * Thrown if the length of iv is longer than the cipher block size. * * @throws NullPointerException (unchecked) * Thrown if iv is null. * * @since 1.3, 2005-04-01 */ protected void engineSetIV(/*const*/ byte[] iv) throws InvalidAlgorithmParameterException { int len; // Check args if (iv == null) throw new NullPointerException("Null cipher IV"); // Copy the IV value, padding it on the right with zeros len = m_blockLen; if (m_iv == null) m_iv = new byte[len]; while (iv.length < len && len-- > 0) m_iv[len] = (byte) 0x00; while (len-- > 0) m_iv[len] = iv[len]; // Check the IV length if (iv.length > m_blockLen) throw new InvalidAlgorithmParameterException( "IV wider than cipher block size, " + iv.length + " > " + m_blockLen); } /*************************************************************************** * Retrieve the block size (in bytes) of this cipher. * * @return * The size, in bytes, of the blocks to use with this cipher. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected int engineGetBlockSize() /*const*/ //overrides javax.crypto.CipherSpi { return (m_blockLen); } /*************************************************************************** * Retrieve the key size (in bytes) of this cipher. * *
* This method is not part of the standard set defined in class * javax.crypto.CipherSpi, but is provided as a convenient * enhancement. * * * @return * The size, in bytes, of the key to use with this cipher. * * @throws UnsupportedOperationException (unchecked) * Thrown if this cipher does not use a fixed key size. * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @since 1.3, 2005-04-01 */ protected int engineGetKeySize() /*const*/ //overrides javax.crypto.CipherSpi { // Sanity check if (!m_init) throw new IllegalStateException("Cipher not initialized"); return (m_keyLen); } /*************************************************************************** * Determine the size of a key for this cipher. * * @param key * A key suitable for use with this cipher. * * @return * Key size (in bits) of the given key. * * @throws InvalidKeyException * Thrown if the given key cannot be used with this cipher. * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @throws UnsupportedOperationException (unchecked) * Thrown if this method is not overridden, or if this cipher does not use a * fixed key size. * * @since 1.3 (JCE 1.2), 2005-04-01 */ protected int engineGetKeySize(Key key) throws InvalidKeyException /*const*/ //overrides javax.crypto.CipherSpi { // This method must be overridden throw new UnsupportedOperationException(); } /*************************************************************************** * Determine the output buffer size of this cipher. * * @param n * Size of the input buffer (in addition to any previous pending input * bytes). * * @return * Output buffer size (in bytes) that will be created by this cipher. * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected abstract int engineGetOutputSize(int n); /*const*/ //overrides javax.crypto.CipherSpi /*************************************************************************** * Retrieve the initial vector (IV) used by this cipher. * * @return * Initial vector bytes used by this cipher (which is an array exactly as * long as the cipher block size), or null if this cipher does not (yet) * have, or need, an IV. * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected byte[] engineGetIV() /*const*/ //overrides javax.crypto.CipherSpi { return (m_iv); } /*************************************************************************** * Retrieve algorithm-specific initialization parameters for this cipher. * * @return * Algorithm-specific initialization parameters (e.g., an IV value as a * javax.crypto.spec.IvParameterSpec object). * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected abstract AlgorithmParameters engineGetParameters(); /*const*/ //overrides javax.crypto.CipherSpi /*************************************************************************** * Encrypt/decrypt an array of bytes using this cipher. * The input bytes are encrypted/decrypted, being appended to any input bytes * that were previously added to this cipher. * * * @param in * Array of bytes to add as input to this cipher. * The data from this array is appended to any input data previously added to * this cipher. * * @param off * Index of the first byte in array in to encrypt/decrypt. * * @param len * Number of bytes in array in to input to encrypt/decrypt. * * @return * Resulting output bytes suitably encrypted/decrypted by this cipher, * or null if the number of input bytes so far is insufficient to produce a * complete output block. * Only complete encrypted/decrypted blocks are returned; partially complete * blocks are not returned, and are kept pending within this cipher. * * @throws IllegalArgumentException (unchecked) * Thrown if len is negative. * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected byte[] engineUpdate(byte[] in, int off, int len) //overrides javax.crypto.CipherSpi { int inLen; int outLen; byte[] out; // Sanity check if (!m_init) throw new IllegalStateException("Cipher not initialized"); // Check args if (len <= 0) { if (len == 0) return (new byte[0]); // Bad input buffer length throw new IllegalArgumentException("Bad input length: " + len); } // Allocate an output buffer inLen = m_inSize+len; // Output buffer length is a multiple of the cipher block size, // and contains only complete output blocks outLen = m_blockLen; if (outLen > 1) outLen = (inLen + outLen-1)/outLen * outLen; out = new byte[outLen]; // Encrypt/decrypt from the input buffer to the output buffer try { engineUpdate(in, off, len, out, 0); } catch (ShortBufferException ex) { // Should not occur, ignore } // Return the output buffer return (out); } /*************************************************************************** * Encrypt/decrypt an array of bytes using this cipher. * The input bytes are encrypted/decrypted, being appended to any input bytes * that were previously added to this cipher. * * * @param in * Array of bytes to add as input to this cipher. * The data from this array is appended to any input data previously added to * this cipher. * * @param inOff * Index of the first byte of array in to input to this cipher. * * @param len * Number of bytes in array in to encrypt/decrypt. * * @param out * Resulting encrypted/decrypted output bytes. * * @param outOff * Index of the first byte in array out to write the resulting * output bytes to. * * @return * Number of output bytes produced by this cipher. * Only complete encrypted/decrypted blocks are written to the output array. * Partially complete blocks are not written, and are kept pending within * this cipher object. * * @throws ShortBufferException * Thrown if the output buffer is too small to hold the resulting data. * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @throws IllegalArgumentException (unchecked) * Thrown if len is negative. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected abstract int engineUpdate(/*const*/ byte[] in, int inOff, int len, byte[] out, int outOff) throws ShortBufferException; //overrides javax.crypto.CipherSpi /*************************************************************************** * Add final input bytes to this cipher. * The added bytes are encrypted/decrypted, being appended to any input bytes * that were previously added to this cipher. * * * @param in * Array of bytes to add as input to this cipher. * * @param off * Index of the first byte in array in to input to this cipher. * * @param len * Number of bytes in array in to input to this cipher. * * @return * Array of bytes suitably encrypted/decrypted by this cipher. * * @throws BadPaddingException * Thrown if the final input block does not contain the appropriate padding * (decrypt mode only). * * @throws IllegalBlockSizeException * Thrown if the total number of input bytes so far is insufficient to * produce a complete output block (encrypt mode only). * * @throws IllegalArgumentException (unchecked) * Thrown if len is negative. * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected byte[] engineDoFinal(byte[] in, int off, int len) throws BadPaddingException, IllegalBlockSizeException //overrides javax.crypto.CipherSpi { int inLen; int outLen; byte[] out; // Sanity check if (!m_init) throw new IllegalStateException("Cipher not initialized"); // Check args if (len < 0) throw new IllegalArgumentException("Bad input length: " + len); // Allocate an output buffer inLen = m_inSize+len; if (inLen == 0) return (new byte[0]); // Output buffer length is a multiple of the cipher block size, // and contains only complete output blocks outLen = m_blockLen; if (outLen > 1) outLen = (inLen + outLen-1)/outLen * outLen; out = new byte[outLen]; // Encrypt/decrypt from the input buffer to the output buffer try { engineDoFinal(in, off, len, out, 0); } catch (ShortBufferException ex) { // Should not occur, ignore } // Return the output buffer return (out); } /*************************************************************************** * Add final input bytes to this cipher. * The added bytes are encrypted/decrypted, being appended to any input bytes * that were previously added to this cipher. * * * @param in * Array of bytes to add as input to this cipher. * * @param inOff * Index of the first byte in array in to input to this cipher. * * @param len * Number of bytes in array in to input to this cipher. * * @param out * Output array to write the resulting encrypted/decrypted bytes to. * * @param outOff * Index of the first byte in array out to write the resulting * output bytes to. * * @return * Number of output bytes produced by this cipher. * * @throws BadPaddingException * Thrown if the final input block does not contain the appropriate padding * (decrypt mode only). * * @throws IllegalBlockSizeException * Thrown if the total number of input bytes so far is insufficient to * produce a complete output block (encrypt mode only). * * @throws ShortBufferException * Thrown if the output buffer is too small to hold the resulting data. * * @throws IllegalArgumentException (unchecked) * Thrown if len is negative. * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @since 1.1 (JCE 1.2), 2003-03-30 */ protected abstract int engineDoFinal(byte[] in, int inOff, int len, byte[] out, int outOff) throws BadPaddingException, IllegalBlockSizeException, ShortBufferException; //overrides javax.crypto.CipherSpi /*************************************************************************** * Wrap a cipher key into a raw encoded form. * * @param key * Key suitable for use with this cipher. * * @return * The key in a raw encoded format. * * @throws IllegalBlockSizeException * Thrown if key is not encoded with a block length supported by * this cipher. * * @throws InvalidKeyException * Thrown if key is not a valid key for this cipher. * * @throws UnsupportedOperationException (unchecked) * Thrown if this operation is not supported by this cipher. * * @see #engineUnwrap engineUnwrap() * * @since 1.1 (JCE 1.3), 2003-03-30 */ protected byte[] engineWrap(Key key) throws IllegalBlockSizeException, InvalidKeyException //overrides javax.crypto.CipherSpi { // This method should be overridden throw new UnsupportedOperationException(); } /*************************************************************************** * Unwrap a cipher key from a raw encoded form. * * @param key * Key in a raw encoded format. * * @return * The key, decoded into a form suitable for use with this cipher. * * @throws InvalidKeyException * Thrown if key is not a valid key for this cipher. * * @throws NoSuchAlgorithmException * Thrown if key is not encoded in a recognizable form. * * @throws UnsupportedOperationException (unchecked) * Thrown if this operation is not supported by this cipher. * * @see #engineWrap engineWrap() * * @since 1.1 (JCE 1.3), 2003-03-30 */ protected Key engineUnwrap(byte[] wrappedKey, String alg, int type) throws InvalidKeyException, NoSuchAlgorithmException //overrides javax.crypto.CipherSpi { // This method should be overridden throw new UnsupportedOperationException(); } /*************************************************************************** * Clear (wipe) this cipher. * Erases all sensitive information contained within this object. * *
* This method is not part of the standard set defined in class * javax.crypto.CipherSpi, but is provided as a convenient * enhancement. * *
* This method should be overridden by classes extending this base class. * It should erase sensitive cryptographic information contained within the * cipher object, such as key schedules, passwords/passphrases, and leftover * plaintext (pre-encrypted or post-decrypted) data blocks. Note that this * kind of information must be written over (with zeros, for example) before * being garbage collected, otherwise it may remain accessible in local * memory for an indeterminate period of time. Some of the cipher * information does not need to be wiped since it does not compromise * security, such as the IV, mode, and padding settings. * *
* The derived overridding method should wipe its sensitive member variables, * and then this method can be called (super.engineClear()) to wipe * the remaining base class members. * *
* Note that this cipher object is no longer usable after calling this * method. * * @since 1.3, 2005-04-01 */ protected void engineClear() { // Wipe sensitive data m_inSize = 0; if (m_in != null) { for (int i = 0; i < m_in.length; i++) m_in[i] = (byte) 0x00; } // Other members should be wiped by the overriding method, // and then call super.engineClear() } /*************************************************************************** * Finalization. * Erases all sensitive information contained within this object prior to it * being garbage-collected. * * @since 1.3, 2004-12-20 */ protected synchronized void finalize() throws Throwable //overrides java.lang.Object { // Wipe the contents of this object engineClear(); // Cascade the finalization super.finalize(); } } // End CipherSpi.java