//============================================================================== // 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