//============================================================================== // SymmetricCipherSpi.java //============================================================================== package tribble.crypto; // System imports import java.lang.Exception; import java.lang.IllegalArgumentException; import java.lang.IllegalStateException; import java.lang.String; 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 java.security.spec.InvalidParameterSpecException; import javax.crypto.BadPaddingException; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; // Local imports // (None) /******************************************************************************* * Symmetric block cipher service provider interface (SPI) implementation. * *

* This class provides the foundation for cipher (encryption) classes having * fixed-length blocks and key lengths and which utilize the same (symmetric, * secret) initialization key for both encryption and decryption. * *

* This class provides supporting methods for handling the encryption and * decryption of data spanning multiple blocks, partial final block handling, * the handling of various padding and operating modes, and so forth. * *

* A block cipher encrypts or decrypts blocks of data, one block at a time. * A "block" of input is a fixed number of bits wide, and is usually 64 to 256 * bits (8 to 32 bytes) in size. * *

* At the heart of a block cipher engine is a symmetric block cipher. * The block cipher may be initialized with an initial vector (IV) which is the * same width as the underlying block cipher, which is used as the first data * block to be encrypted by the cipher. * The IV should be a random value, but it does not have to be kept secret; in * fact, it is typically prepended to the encrypted output data stream or * otherwise transmitted along with the encrypted data, so that the decrypting * side uses the same IV value. * *

* At each stage in the block ciphering process, the output of the cipher may be * combined with other blocks within the cipher, depending on the operating * mode of the cipher. * *

* The following block encryption modes are supported: *

* *

* The final block input to the cipher may be padded so that it is same width * as the underlying block size. This entails appending extra bytes to the end * of the final (partial) data block, which depends on the padding scheme * used by the cipher. * *

* The following padding schemes are supported: *

* *
  • ECB - electronic codebook (no chaining) *

    * Note that none of these methods are synchronized. * * * @version $Revision: 1.5 $ $Date: 2005/07/10 22:38:26 $ * @since JCE 1.2 / JRE 1.4, 2002-04-25 * @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 AsymmetricCipherSpi * @see StreamCipherSpi * @see SymmetricCipher */ public class SymmetricCipherSpi extends tribble.crypto.CipherSpi { // Identification /** Revision information. */ static final String REV = "@(#)tribble/crypto/SymmetricCipherSpi.java $Revision: 1.5 $ $Date: 2005/07/10 22:38:26 $\n"; /** Class series version. */ public static final int SERIES = 200; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Package private constants //---------------------------------- // Operating mode control bitflags /** Operating mode bitflag: No feedback action. */ static final short FB_NONE = 0x0000; /** Operating mode bitflag: XOR input block with IV before encrypting. */ static final short FB_XOR_PRE = 0x0001; /** Operating mode bitflag: XOR output block with IV after encrypting. */ static final short FB_XOR_POST = 0x0002; /** Operating mode bitflag: Plaintext block becomes next IV. */ static final short FB_IV_SETP = 0x0004; /** Operating mode bitflag: Ciphertext block becomes next IV. */ static final short FB_IV_SETC = 0x0008; /** Operating mode bitflag: XOR previous ciphertext block with next IV. */ static final short FB_IV_XORC = 0x0010; /** Operating mode bitflag: Use ciphertext stealing on the last block. */ static final short FB_CT_STEAL = 0x0080; //---------------------------------- // Operating mode control bitflag combinations /** Operating mode (block): ECB, electronic codebook (none). */ static final int MODEFLAG_ECB = FB_NONE; /** Operating mode (block): CBC, cipher block chaining. */ static final int MODEFLAG_CBC = FB_XOR_PRE | FB_IV_SETC; /** Operating mode (block): PBC, plaintext block chaining. */ static final int MODEFLAG_PBC = FB_XOR_PRE | FB_IV_SETP; /** Operating mode (block): PCBC, propagating cipher block chaining. */ static final int MODEFLAG_PCBC = FB_XOR_PRE | FB_IV_SETP | FB_IV_XORC; /** Operating mode (block): BC, block chaining. */ static final int MODEFLAG_BC = FB_XOR_PRE | FB_IV_XORC; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected static methods /*+++INCOMPLETE, REDO /*************************************************************************** * Test driver. * *

    * Usage * * java tribble.crypto.SomeCipher * * * * @param ciph * Derived class cipher object to test. * * @throws Exception * Thrown if an error occurs in the encryption engine. * * @since 1.2, 2003-04-05 *\/ +++INCOMPLETE, REDO protected static final void test(SymmetricCipherSpi ciph) throws Exception { byte[] e; byte[] d; int blen; boolean pass; // Initialize the cipher with an encryption key System.out.println("Initialize the cipher for encryption"); System.out.println("Key:"); HexDump.hexDump(System.out, key); ciph.initialize??(key, false); ciph.m_decrypt = false; blen = ciph.m_blockLen; // Encrypt the test vector System.out.println("Input data:"); HexDump.hexDump(System.out, input); if (input.length < blen) throw new Exception("Input block length must be " + blen); e = new byte[ciph.m_blockLen]; ciph.blockEncrypt(input, 0, e, 0); System.out.println("Encrypted data:"); HexDump.hexDump(System.out, e); System.out.println(); // Prepare to decrypt the encrypted test vector System.out.println("Re-Initialize the cipher for decryption"); System.out.println("Key:"); HexDump.hexDump(System.out, key); ciph.initialize??(key, true); ciph.m_decrypt = true; blen = ciph.m_blockLen; // Decrypt the encrypted test vector System.out.println("Input data:"); HexDump.hexDump(System.out, e); if (e.length < blen) throw new Exception("Encrypted block length must be " + blen); d = new byte[ciph.m_blockLen]; ciph.blockDecrypt(e, 0, d, 0); System.out.println("Decrypted data:"); HexDump.hexDump(System.out, d); System.out.println(); // Verify the results pass = true; for (int i = 0; i < blen; i++) if (d[i] != input[i]) pass = false; System.out.println((pass ? ">>> PASS" : ">>> FAIL")); System.out.println(); } +++*/ /*************************************************************************** * Encrypt/decrypt a data file. * *

    * Usage * * java tribble.crypto.SomeCipher * [-option...] in-file * * *

    * Options: *

    *

    *
    -e *
    Encrypt the input file. * *
    -d *
    Decrypt the intput file. * *
    -h *
    If encrypting, include information about the input file * (modification date) in the encrypted output. * If decrypting, extract the file information from the decrypted output * and set the output file attributes (modification date) accordingly. * *
    -o out-file *
    Output file. * If this option is not specified, the output is written to the standard * output by default. * *
    -p passphrase *
    Encryption/decryption passphrase. * *
    -pf passphrase-file *
    Text file containing the encryption/decryption passphrase. *
    * *

    * Either the -e or the -d must be specified. * *

    * If neither the -p nor the -pf option is specified, the * encryption/decryption passphrase is read from the standard input after a * prompt is written to the standard error output. * * * @param ciph * Derived class cipher object to test. * * @param args * Command line arguments. * * @throws Exception * Thrown if an error occurs in the encryption engine. * * @since 1.5, 2005-04-07 */ protected static void run(SymmetricCipherSpi ciph, String[] args) throws Exception { /*+++INCOMPLETE ...; +++*/ } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Package private variables /** Underlying primitive block cipher. */ SymmetricCipher m_ciph; /** Operating mode control bitflags (see {@link #m_mode}). */ int m_modeFlags = MODEFLAG_ECB; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected constructors /*************************************************************************** * Constructor. * * @param ciph * Underlying block cipher. * * @param mode * Operating mode, which must match one of the * {@link #MODE_ECB 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, 2005-07-07 */ protected SymmetricCipherSpi(SymmetricCipher ciph, String mode, String pad) throws NoSuchAlgorithmException, NoSuchPaddingException { // Initialize super(ciph.getAlgorithm(), mode, pad); m_ciph = ciph; m_blockLen = ciph.getBlockSize(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Protected methods /*************************************************************************** * 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. * *

    * Note that this cipher object is no longer usable after calling this * method. * * @since 1.3, 2005-04-01 */ protected void engineClear() //overriddes tribble.crypto.CipherSpi { // Wipe sensitive data m_ciph.clear(); super.engineClear(); } /*************************************************************************** * Initialize this cipher. * *

    * This method invokes the {@link #initialize} method of the underlying * block cipher. * * * @param mode * Ciphering mode, which is either {@link #ENCRYPT_MODE} or * {@link #DECRYPT_MODE}. * * @param key * Symmetric (secret) 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. * * @see #engineGetParameters() engineGetParameters() * @see #engineGetIV engineGetIV() * @see #engineSetIV engineSetIV() * @see #initialize * * @since 1.2, 2003-04-04 */ protected void engineInit(int mode, Key key, SecureRandom rand) throws InvalidKeyException //overrides tribble.crypto.CipherSpi { try { // Initialize this cipher engineInit(mode, key, (AlgorithmParameterSpec) null, rand); } catch (InvalidAlgorithmParameterException ex) { // Should never occur, ignore } } /*************************************************************************** * Initialize this cipher. * *

    * Note that if this cipher is being initialized for decryption, it must be * provided with the same algorithm parameters that were used to initialize * the cipher that originally encrypted the data that will be fed into 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. * * @see #engineGetParameters() engineGetParameters() * @see #engineGetIV engineGetIV() * @see #engineSetIV engineSetIV() * @see #initialize * * @since 1.2, 2003-04-05 */ protected void engineInit(int mode, Key key, AlgorithmParameters parms, SecureRandom rand) throws InvalidKeyException, InvalidAlgorithmParameterException //overrides tribble.crypto.CipherSpi { IvParameterSpec ivSpec = null; // Set up the IV from the parameters if (parms != null) { try { // Extract the IV from the parameters, if it is present ivSpec = (IvParameterSpec) parms.getParameterSpec(IvParameterSpec.class); } catch (InvalidParameterSpecException ex) { // Bad IV spec throw new InvalidAlgorithmParameterException(ex.getMessage()); } } // Initialize this cipher engineInit(mode, key, ivSpec, rand); } /*************************************************************************** * Initialize this cipher. * *

    * Note that if this cipher is being initialized for decryption, it must be * provided with the same algorithm parameters that were used to initialize * the cipher that originally encrypted the data that will be fed into 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). If this parameter is * null and mode is ENCRYPT_MODE, the IV is initialized * from the random source rand; otherwise if mode is * DECRYPT_MODE, the IV is initialized to all zeros. * * @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 * (i.e., a javax.crypto.spec.SecretKeySpec) for use with this * cipher. * * @throws InvalidAlgorithmParameterException * Thrown if parms is not a proper initialization parameter for this * cipher. * * @throws IllegalArgumentException (unchecked) * Thrown if mode is not a valid value. * * @see #engineGetParameters() engineGetParameters() * @see #engineGetIV engineGetIV() * @see #engineSetIV engineSetIV() * @see #initialize * * @since 1.2, 2003-04-05 */ protected void engineInit(int mode, Key key, AlgorithmParameterSpec parms, SecureRandom rand) throws InvalidKeyException, InvalidAlgorithmParameterException //overrides tribble.crypto.CipherSpi { SecretKeySpec kspec; // Set up the mode if (mode != ENCRYPT_MODE && mode != DECRYPT_MODE) throw new IllegalArgumentException( "Cipher mode must be ENCRYPT_MODE or DECRYPT_MODE"); m_decrypt = (mode == DECRYPT_MODE); // Set up the encryption/decryption key if (!(key instanceof SecretKeySpec)) throw new InvalidKeyException("Key is not a SecretKeySpec"); kspec = (SecretKeySpec) key; // Set up the random number source if (rand == null) { if (s_rand == null) { // Create/initialize the default random source synchronized (this.getClass()) { if (s_rand == null) s_rand = new SecureRandom(); } } // Use the default random source rand = s_rand; } m_rand = rand; // Initialize the underlying cipher m_ciph.initialize(kspec.getEncoded(), m_decrypt); m_alg = m_ciph.m_alg; m_blockLen = m_ciph.m_blockLen; m_keyLen = m_ciph.m_keyLen; // Initialize the IV block m_iv = new byte[m_blockLen]; if (parms != null) { if (parms instanceof IvParameterSpec) { IvParameterSpec ivSpec; // Initialize the IV block ivSpec = (IvParameterSpec) parms; engineSetIV(ivSpec.getIV()); } else { // Invalid algorithm parameter, expecting an IV throw new InvalidAlgorithmParameterException( "Expecting an IvParameterSpec"); } } else if (!m_decrypt) { // Initialize the IV block from the random source m_rand.nextBytes(m_iv); } // Initialize the internal block buffers m_in = new byte[m_blockLen]; m_inSize = 0; m_init = true; } /*************************************************************************** * Set the operating mode of this cipher. * * @param mode * Cipher operating mode to use * (e.g., "ECB", "CBC", "CFB", "OFB", * "PCBC", "BC", etc.). * The default mode is "ECB". * See the {@link #MODE_ECB MODE_XXX} constants. * * @throws NoSuchAlgorithmException * Thrown if mode does not specify a operating mode supported by * this cipher. * * @throws NullPointerException (unchecked) * Thrown if mode is null. * * @since 1.2, 2003-03-30 */ protected void engineSetMode(String mode) throws NoSuchAlgorithmException //overrides tribble.crypto.CipherSpi { // Check args if (mode == null) throw new NullPointerException("Null cipher mode"); // Compare the operating mode to supported modes if (mode.equalsIgnoreCase(MODE_ECB)) { m_mode = MODE_ECB; m_modeFlags = MODEFLAG_ECB; } else if (mode.equalsIgnoreCase(MODE_CBC)) { m_mode = MODE_CBC; m_modeFlags = MODEFLAG_CBC; } else if (mode.equalsIgnoreCase(MODE_PBC)) { m_mode = MODE_PBC; m_modeFlags = MODEFLAG_PBC; } else if (mode.equalsIgnoreCase(MODE_PCBC)) { m_mode = MODE_PCBC; m_modeFlags = MODEFLAG_PCBC; } else if (mode.equalsIgnoreCase(MODE_BC)) { m_mode = MODE_BC; m_modeFlags = MODEFLAG_BC; } else if (mode.equalsIgnoreCase("None") || mode.equals("")) { m_mode = MODE_ECB; m_modeFlags = MODEFLAG_ECB; } else { // Unsupported cipher operating mode throw new NoSuchAlgorithmException("Unsupported cipher mode: '" + mode + "'"); } } /*************************************************************************** * Set the padding scheme of this cipher. * * @param padding * Cipher padding scheme to use (e.g., "PKCS5Padding"). * The default scheme is "NoPadding". * See the {@link #PADDING_NONE PADDING_XXX} constants. * * @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.2, 2003-04-05 */ protected void engineSetPadding(String padding) throws NoSuchPaddingException //overrides tribble.crypto.CipherSpi { // Check args if (padding == null) throw new NullPointerException("Null cipher padding"); // Compare the padding scheme to supported schemes if (padding.equalsIgnoreCase(PADDING_NONE)) m_padding = PADDING_NONE; else if (padding.equalsIgnoreCase(PADDING_PKCS5)) m_padding = PADDING_PKCS5; else if (padding.equalsIgnoreCase(PADDING_ZEROS)) m_padding = PADDING_ZEROS; /*+++NOT SUPPORTED YET else if (padding.equalsIgnoreCase(PADDING_CTS)) m_padding = PADDING_CTS; +++*/ else if (padding.equalsIgnoreCase("None") || padding.equals("")) m_padding = PADDING_NONE; else throw new NoSuchPaddingException("Unsupported cipher padding: " + padding); } /*************************************************************************** * 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. * * @throws NullPointerException (unchecked) * Thrown if key is null. * * @since 1.2, 2003-04-05 */ protected int engineGetKeySize(Key key) throws InvalidKeyException /*const*/ //overrides tribble.crypto.CipherSpi { SymmetricKey kspec; // Sanity check if (!m_init) throw new IllegalStateException("Cipher not initialized"); // Check args if (key == null) throw new NullPointerException("Null cipher key"); // Determine the key size for the given key if (!(key instanceof SymmetricKey)) throw new InvalidKeyException("Key is not a SymmetricKey"); kspec = (SymmetricKey) key; return (kspec.m_key.length); } /*************************************************************************** * 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.2, 2003-04-05 */ protected int engineGetOutputSize(int n) /*const*/ //overrides tribble.crypto.CipherSpi { int len; // Sanity check if (!m_init) throw new IllegalStateException("Cipher not initialized"); // Compute the output buffer size for the given input buffer size if (m_padding == PADDING_CTS) { // Padding uses ciphertext stealing len = m_inSize+n; } else { // Pad the last block to be a multiple of the cipher block size len = m_blockLen; len = (m_inSize+n + len-1)/len * len; } return (len); } /*************************************************************************** * Retrieve algorithm-specific initialization parameters for this cipher. * * @return * Algorithm-specific initialization parameters. * * @throws IllegalStateException (unchecked) * Thrown if this cipher has not been initialized. * * @since 1.2, 2003-04-05 */ protected AlgorithmParameters engineGetParameters() /*const*/ //overrides tribble.crypto.CipherSpi { // Can be overridden by classes extending this class /*+INCOMPLETE m_ciph.xxx(...); +*/ return (null); } /*************************************************************************** * Encrypt/decrypt an array of bytes using this cipher. * *

    * This method handles blocking and padding of the input data, operating * modes, and so forth required of symmetric block ciphers. * * *

    * Encryption *

    *

        * Input:
        *          |- - - - - - - - - input length  - - - - - - - - - -|
        *          :                                                   :
        *          :             plaintext input blocks                :
        *    += = =:=====+=========+=========+===/ /===+=========+=====:= = =+
        *    |pend :XXXXX|XXXXXXXXX|XXXXXXXXX|  . . .  |XXXXXXXXX|XXXXX:     |
        *    += = =:=====+=========+=========+===/ /===+=========+=====:= = =+
        *    :     :                                             :     :
        *    :     :                                             :     :
        *    |- - -| previous pending input length               :     :
        *    :                                                   :     :
        *    |- - - - - - - - - total input length  - - - - - - - - - -|
        *    :                                                   :     :
        *    |- - - - - - - complete input blocks - - - - - - - -|     :
        *                                                        :     :
        *                          leftover pending input length |- - -|
        *
        * Output:
        *                       ciphertext output blocks
        *    +===========+=========+=========+===/ /===+=========+= = =:= = =+
        *    |XXXXXXXXXXX|XXXXXXXXX|XXXXXXXXX|  . . .  |XXXXXXXXX|pend :     |
        *    +===========+=========+=========+===/ /===+=========+= = =:= = =+
        *    :                                                   :     :
        *    :                                                   :     :
        *    |- - - - - - - - - - output length  - - - - - - - - |     :
        *                                                        :     :
        *                               new pending input length |- - -| 
    * *

    * Note that only complete input blocks are encrypted, resulting in the * cumulative output length being a multiple of the cipher block size. * Leftover partial blocks are saved and prepended to the next group of input * bytes to be encrypted. * * *

    * Decryption *

    *

        * Input:
        *          |- - - - - - - - - input length  - - - - - - - - - -|
        *          :                                                   :
        *          :             ciphertext input blocks               :
        *    += = =:=====+=========+=========+===/ /===+=========+=====:= = =+
        *    |pend :XXXXX|XXXXXXXXX|XXXXXXXXX|  . . .  |XXXXXXXXX|XXXXX:     |
        *    += = =:=====+=========+=========+===/ /===+=========+=====:= = =+
        *    :     :                                             :     :
        *    :     :                                             :     :
        *    |- - -| previous pending input length               :     :
        *    :                                                   :     :
        *    |- - - - - - - - complete input blocks - - - - - - -|     :
        *                                                        :     :
        *                          leftover pending input length |- - -|
        *
        * Output:
        *                        plaintext output blocks
        *    +===========+=========+=========+===/ /===+=========+=====:= = =+
        *    |XXXXXXXXXXX|XXXXXXXXX|XXXXXXXXX|  . . .  |XXXXXXXXX|XXXXX:     |
        *    +===========+=========+=========+===/ /===+=========+=====:= = =+
        *    :                                                   :     :
        *    :                                                   :     :
        *    |- - - - - - - - - - output length  - - - - - - - - |     :
        *                                                        :     :
        *                       new pending partial output block |- - -| 
    * *

    * Note that only complete input blocks are decrypted, resulting in the * cumulative output length being a multiple of the cipher block size. * Leftover partial blocks are saved and prepended to the next group of input * bytes to be decrypted. * * * @param in * Array of bytes to encrypt/decrypt. * The data from this array is appended to any input data previously added to * this cipher. * * @param inOff * Index of the first byte in array in to encrypt/decrypt. * * @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 encrypted/decrypted bytes written into array out. * 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.2, 2003-04-03 */ protected int engineUpdate(/*const*/ byte[] in, int inOff, int len, byte[] out, int outOff) throws ShortBufferException //overrides tribble.crypto.CipherSpi { // Sanity check if (!m_init) throw new IllegalStateException("Cipher not initialized"); // Check args if (len <= 0) { if (len == 0) return (0); // Bad input buffer length throw new IllegalArgumentException("Bad input length: " + len); } // Encrypt/decrypt the input buffer if (m_decrypt) return (decryptData(in, inOff, len, out, outOff)); else return (encryptData(in, inOff, len, out, outOff)); } /*************************************************************************** * 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. * *

    * See * {@link #engineDoFinal(byte[], int, int len, byte[], int) engineDoFinal()} * for more details. * * * @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.2, 2003-04-05 */ /*+++INCOMPLETE, might be obsolete, default to super.engineDoFinal() protected byte[] engineDoFinal(/*const*\/ byte[] in, int off, int len) throws BadPaddingException, IllegalBlockSizeException //overrides tribble.crypto.CipherSpi { return (super.engineDoFinal(in, off, len); /*+++INCOMPLETE, might be obsolete ///+USE super.engineDoFinal() instead int inLen; int outLen; byte[] outBuf; // 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 composed of complete blocks inLen = m_inSize+len; if (inLen == 0) return (new byte[0]); outBuf = null; +INCOMPLETE if (m_decrypt) { // Check decryption buffer size if (m_padding == PADDING_NONE) { // The input buffer must be a multiple of the cipher block size if (inLen % m_blockLen != 0) throw new BadPaddingException( "Decrypt buffer must be a multiple of " + m_blockLen + " in size"); outLen = inLen; } else { for decrypt only: // Output buffer length is a multiple of the cipher block size, // and contains only complete output blocks, plus room for padded // encrypted padded output blocks outLen = m_blockLen; outLen = inLen/outLen * outLen; // Might be zero ...handle extra padding block outBuf = new byte[outLen]; } } else { // Check encryption buffer size if (m_padding == PADDING_NONE) { // The input buffer must be a multiple of the cipher block size if (inLen % m_blockLen != 0) throw new IllegalBlockSizeException( "Encrypt buffer must be a multiple of " + m_blockLen + " in size"); outLen = inLen; } // Output buffer length is a multiple of the cipher block size, // and contains only complete output blocks outLen = ...; outBuf = new byte[outLen]; } // Encrypt/decrypt from the input buffer to the output buffer try { engineDoFinal(in, off, len, outBuf, 0); } catch (ShortBufferException ex) { // Should not occur, ignore } // Return the output buffer return (outBuf); } +++*/ /*************************************************************************** * 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. * * *

    * Encryption *

    *

        * Input:
        *          |- - - - - - - - final input length  - - - - - - - -|
        *          :                                                   :
        *    |- - -| previous pending input length                     :
        *    :     :                                                   :
        *    :     :             plaintext input blocks                :
        *    += = =:=====+=========+=========+===/ /===+=========+=====:= = =+
        *    |pend :XXXXX|XXXXXXXXX|XXXXXXXXX|  . . .  |XXXXXXXXX|XXXXX: pad |
        *    += = =:=====+=========+=========+===/ /===+=========+=====:= = =+
        *    :                                                         :     :
        *    :                                                         :     :
        *    :                                     final block padding |- - -|
        *    :                                                               :
        *    |- - - - - - - - - - complete input blocks - - - - - - - - - - -|
        *
        * Output:
        *                       ciphertext output blocks
        *    +===========+=========+=========+===/ /===+=========+===========+
        *    |XXXXXXXXXXX|XXXXXXXXX|XXXXXXXXX|  . . .  |XXXXXXXXX|XXXXXXXXXXX|
        *    +===========+=========+=========+===/ /===+=========+===========+
        *    :                                                               :
        *    :                                                               :
        *    |- - - - - - - - - - final output length - - - - - - - - - - - -|
    * *

    * Note that the final output length results in the total number of output * bytes being a multiple of the cipher block size. * * *

    * Decryption *

    *

        * Input:
        *          |- - - - - - - - - - final input length  - - - - - - - - -|
        *          :                                                         :
        *          :               ciphertext input blocks                   :
        *    += = =:=====+=========+=========+===/ /===+=========+===========+
        *    |pend :XXXXX|XXXXXXXXX|XXXXXXXXX|  . . .  |XXXXXXXXX|XXXXXXXXXXX|
        *    += = =:=====+=========+=========+===/ /===+=========+===========+
        *    :     :                                                         :
        *    :     :                                                         :
        *    |- - -| previous pending input length                           :
        *    :                                                               :
        *    |- - - - - - - - - - - complete input blocks - - - - - - - - - -|
        *
        * Output:
        *                        plaintext output blocks
        *    +===========+=========+=========+===/ /===+=========+=====:= = =+
        *    |XXXXXXXXXXX|XXXXXXXXX|XXXXXXXXX|  . . .  |XXXXXXXXX|XXXXX: pad |
        *    +===========+=========+=========+===/ /===+=========+=====:= = =+
        *    :                                                         :     :
        *    :                                                         :     :
        *    :                                       truncated padding |- - -|
        *    :                                                         :
        *    |- - - - - - - - - final output length - - - - - - - - - -| 
    * *

    * Note that the final input length must result in the total number of input * bytes being a multiple of the cipher block size. * * * @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.2, 2003-04-05 */ protected int engineDoFinal(/*const*/ byte[] in, int inOff, int len, byte[] out, int outOff) throws BadPaddingException, IllegalBlockSizeException, ShortBufferException //overrides tribble.crypto.CipherSpi { int inLen; int outLen; // Sanity check if (!m_init) throw new IllegalStateException("Cipher not initialized"); // Check args if (len < 0) throw new IllegalArgumentException("Bad buffer length: " + len); inLen = m_inSize+len; /*+++INCOMPLETE ...handle extra padding block if (inLen == 0)? throw new IllegalArgumentException("Bad buffer length: " + len); +++*/ /*+++INCOMPLETE ...handle extra padding block; // Handle padding if (m_inSize < m_blockLen || m_padding == PADDING_PKCS5) { // Handle padding of the last input block if (m_padding == PADDING_PKCS5) { // Pad the last block (partial or not), PKCS#5 ...; } else { // Pad the last partial block with zeros ...; } } +++*/ // Encrypt/decrypt the input buffer, updating this cipher if (m_decrypt) outLen = decryptData(in, inOff, len, out, outOff); else outLen = encryptData(in, inOff, len, out, outOff); inOff += len; outOff += outLen; /*+++INCOMPLETE ...; +++*/ // Encrypt/decrypt the input buffer /*+++INCOMPLETE outLen = ...; ...check that out[] is long enough for all output blocks; +REDO, calling engineUpdate(in, inOff, outLen, out, outOff) if (m_decrypt) return (decryptData(in, inOff, len, out, outOff)); else return (encryptData(in, inOff, len, out, outOff)); +REDO, handling the final complete/partial block, if any +++*/ return (0); } /*************************************************************************** * Finalization. * Erases all sensitive information contained within this object prior to it * being garbage-collected. * * @since 1.4, 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(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // Private methods /*************************************************************************** * Encrypt an array of bytes using this cipher. * *

    * This method handles blocking of the input data, operating modes, and so * forth required of generic symmetric block ciphers. * *

    * The various cipher modes are handled by this method. *

        * Encrypting:
        *                     +---------+
        *                     |XXXXXXXXX| IV
        *                     +---------+   i
        *                          |
        *                     +----+-------------+
        *                     |    | a           | b
        *    +---------+      |    v             v        +---------+
        * P  |XXXXXXXXX|---+----->(+)--->[E]-->(+)---+--->|XXXXXXXXX| C
        *  i +---------+   |  |                      |    +---------+  i
        *                  |  +-----+                |
        *                  |        |                |
        *                  |        v                |
        *                  | c +---------+  d        |
        *                  +-->|XXXXXXXXX|<----------+
        *                      +---------+           |
        *                           |                |
        *                           v   e            |
        *                          (+)<--------------+
        *                           |
        *                           v
        *                      +---------+
        *                      |XXXXXXXXX| IV
        *                      +---------+   i+1 
    * *

    * Operations: *

    * * * @param in * Array of bytes to encrypt. * * @param inOff * Index of the first byte of array in to encrypt. * * @param len * The number of bytes in array in to encrypt. * * @param out * Resulting encrypted output bytes. * * @param outOff * Index of the first byte of array out to write. * * @return * Number of encrypted bytes written into array out. * Only complete encrypted blocks are written to the output array. * Partially complete blocks are not written, and are kept pending within * this cipher. * * @throws ShortBufferException * Thrown if the output buffer is too small to hold the resulting data. * * @see #engineUpdate(byte[], int, int, byte[], int) engineUpdate() * @see #engineDoFinal(byte[], int, int, byte[], int) engineDoFinal() * @see #decryptData decryptData() * * @since 1.4, 2003-04-09 */ private int encryptData(/*const*/ byte[] in, int inOff, int len, byte[] out, int outOff) throws ShortBufferException { int blockLen; int flags; int max; int i; int o; // Check that the output buffer is big enough if (outOff+len > out.length) throw new ShortBufferException("Output buffer too short: " + out.length + " < " + (outOff+len)); // Copy input bytes into the input buffer flags = m_modeFlags; blockLen = m_blockLen; max = inOff+len; for (i = inOff, o = outOff; i < max; ) { // Append input bytes to the input buffer while (m_inSize < blockLen && i < max) m_in[m_inSize++] = in[i++]; // Check for a complete input block if (m_inSize >= blockLen) { // Handle operating modes if ((flags & FB_XOR_PRE) != 0) { // Propagate the CBC/PCB feedback vector for (int k = blockLen; k-- > 0; ) m_in[k] ^= m_iv[k]; } // Encrypt a single complete plaintext block m_ciph.blockEncrypt(m_in, 0, out, o); // Handle operating modes if ((flags & FB_IV_SETC) != 0) { // Propagate the CBC feedback vector for (int k = blockLen; k-- > 0; ) m_iv[k] = out[o+k]; } if ((flags & FB_IV_SETP) != 0) { byte[] t; // Propagate the PBC feedback vector t = m_iv; m_iv = m_in; m_in = t; } if ((flags & FB_IV_XORC) != 0) { // Propagate the PCBC/BC feedback vector for (int k = blockLen; k-- > 0; ) m_iv[k] ^= out[o+k]; } // Set up for the next input block o += blockLen; m_inSize = 0; } } // Return the total number of encrypted output bytes return (o-outOff); } /*************************************************************************** * Decrypt an array of bytes using this cipher. * *

    * This method handles blocking of the input data, operating modes, and so * forth required of generic symmetric block ciphers. * *

    * The various cipher modes are handled by this method. *

        * Decrypting:
        *                     +---------+
        *                     |XXXXXXXXX| IV
        *                     +---------+   i
        *                          |
        *                     +----+-------------+
        *                     |    | b           | a
        *    +---------+      |    v             v        +---------+
        * C  |XXXXXXXXX|--+------>(+)--->[D]--->(+)--+--->|XXXXXXXXX| P'
        *  i +---------+  |   |                      |    +---------+  i
        *                 |   +----+                 |
        *                 |        |                 |
        *                 |        v                 |
        *                 | d +---------+  c         |
        *                 +-->|XXXXXXXXX|<-----------+
        *                 |   +---------+
        *                 |        |
        *                 | e      v
        *                 +------>(+)
        *                          |
        *                          v
        *                     +---------+
        *                     |XXXXXXXXX| IV
        *                     +---------+   i+1 
    * *

    * Operations: *

    * * * @param in * Array of bytes to decrypt. * * @param inOff * Index of the first byte of array in to decrypt. * * @param len * The number of bytes in array in to decrypt. * * @param out * Resulting decrypted output bytes. * * @param outOff * Index of the first byte of array out to write. * * @return * Number of decrypted bytes written into array out. * Only complete decrypted blocks are written to the output array. * Partially complete blocks are not written, and are kept pending within * this cipher. * * @throws ShortBufferException * Thrown if the output buffer is too small to hold the resulting data. * * @see #engineUpdate(byte[], int, int, byte[], int) engineUpdate() * @see #engineDoFinal(byte[], int, int, byte[], int) engineDoFinal() * @see #encryptData encryptData() * * @since 1.4, 2003-04-09 */ private int decryptData(/*const*/ byte[] in, int inOff, int len, byte[] out, int outOff) throws ShortBufferException { int blockLen; int flags; int max; int i; int o; // Check that the output buffer is big enough if (outOff+len > out.length) throw new ShortBufferException("Output buffer too short: " + out.length + " < " + (outOff+len)); // Copy input bytes into the input buffer flags = m_modeFlags; blockLen = m_blockLen; max = inOff+len; for (i = inOff, o = outOff; i < max; ) { // Append input bytes to the input buffer while (m_inSize < blockLen && i < max) m_in[m_inSize++] = in[i++]; /*+INCOMPLETE +MOVE to doFinal():decrypt if (isFinal) { // Unpad the final block if (m_inSize+blockLen > max && m_padding == PADDING_PKCS5) { int pad; // Pad the final partial block (PKCS#5) pad = blockLen-m_inSize; while (m_inSize < blockLen) m_in[m_inSize++] = pad; } } +*/ // Check for a complete input block if (m_inSize >= blockLen) { // Decrypt a single complete plaintext block m_ciph.blockDecrypt(m_in, 0, out, o); // Handle operating modes if ((flags & FB_XOR_PRE) != 0) { // Propagate the CBC/PCB feedback vector for (int k = blockLen; k-- > 0; ) out[o+k] ^= m_iv[k]; } if ((flags & FB_IV_SETC) != 0) { byte[] t; // Propagate the CBC feedback vector t = m_iv; m_iv = m_in; m_in = t; } if ((flags & FB_IV_SETP) != 0) { // Propagate the PBC feedback vector for (int k = blockLen; k-- > 0; ) m_iv[k] = out[o+k]; } if ((flags & FB_IV_XORC) != 0) { // Propagate the PCBC/BC feedback vector for (int k = blockLen; k-- > 0; ) m_iv[k] ^= m_in[k]; } // Set up for the next input block o += blockLen; m_inSize = 0; } } // Return the total number of decrypted output bytes return (o-outOff); } } // End SymmetricCipherSpi.java