/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * *********************************************************************************/ package totalcross.crypto.cipher; import totalcross.crypto.*; import totalcross.io.ByteArrayStream; /** * This class provides the functionality of a cryptographic cipher for encryption * * <p>If you get a <code>totalcross.crypto.CryptoException: Illegal key size</code>, you must download the strong cryptography files from Oracle * site. In order to do that, go to the ReadMe file whole link is below the download link. In this file, search for "Unlimited Strength Java * Cryptography Extension" and follow the instructions. */ public abstract class Cipher { Object cipherRef; Object keyRef; protected int operation = -1; protected Key key; protected int chaining; protected byte[] iv; protected int padding; protected Object nativeHeap; private ByteArrayStream input = new ByteArrayStream(128); private byte[] oneByte = new byte[1]; /** * Constant used to initialize cipher to encrypt. */ public static final int OPERATION_ENCRYPT = 0; /** * Constant used to initialize cipher to decrypt. */ public static final int OPERATION_DECRYPT = 1; /** * Constant used to initialize cipher using no chaining. */ public static final int CHAINING_NONE = 0; /** * Constant used to initialize cipher using ECB chaining. */ public static final int CHAINING_ECB = 1; /** * Constant used to initialize cipher using CBC chaining. */ public static final int CHAINING_CBC = 2; /** * Constant used to initialize cipher using no padding. */ public static final int PADDING_NONE = 0; /** * Constant used to initialize cipher using PKCS #1 padding. */ public static final int PADDING_PKCS1 = 1; /** * Constant used to initialize cipher using PKCS #5 padding.<br> * Comment about PKCS#5 padding:<br><br> * * PKCS#5 padding is a padding scheme that always adds padding bytes even if the input size is a multiple of the block size (i. e. 16 bytes). The * value of the padding bytes is the number of bytes added to get a multiple of the block size. Thus, once deciphered, you can immediately find * out how many padding bytes have been added by looking the value of the last byte of the output buffer. This is always true since there must * always be at least one padding byte.<br><br> * * See <a href='http://tools.ietf.org/html/rfc3852#section-6.3'>http://tools.ietf.org/html/rfc3852#section-6.3</a> for more details.<br><br> * * For instance, if you use PKCS#5 with a 16 bytes input buffer, 16 values of 0x10 will be added resulting in 2 input blocks of 16 bytes each * (32 bytes). Once deciphered, the 16 bytes padding block will be removed to return to the initial 16 bytes input buffer. */ public static final int PADDING_PKCS5 = 2; /** * Returns the name of the algorithm. * * @return The name of the algorithm whose class heirs from Cipher. */ public final String toString() { return getAlgorithm(); } /** * Returns the initialization vector (IV) in a new buffer. This is useful in the case where a random IV was created. * * @return The initialization vector in a new buffer, or <code>null</code> if the underlying algorithm does not use an IV, or if the IV has not * been set yet. */ public final byte[] getIV() { return iv; } /** * Initializes this cipher in encryption or decryption mode, without chaining or padding. If this algorithm requires an initialization vector, it * will be generated using random values. Calling this method will also reset the input data buffer. * * @param operation The operation mode of this cipher (<code>OPERATION_ENCRYPT</code> or <code>OPERATION_DECRYPT</code>). * @param key The key. * * @throws CryptoException If one or more initialization parameters are invalid or the cipher fails to initialize with the given parameters. */ public final void reset(int operation, Key key) throws CryptoException { reset(operation, key, CHAINING_NONE, null, PADDING_NONE); } /** * Initializes this cipher in encryption or decryption mode, with the given chaining mode and without a padding. If this algorithm requires an * initialization vector, it will be generated using random values. Calling this method will also reset the input data buffer. * * @param operation The operation mode of this cipher (<code>OPERATION_ENCRYPT</code> or <code>OPERATION_DECRYPT</code>). * @param key The key. * @param chaining The chaining mode of this cipher (<code>CHAINING_NONE</code>, <code>CHAINING_ECB</code>, or * <code>CHAINING_CBC</code>). * * @throws CryptoException If one or more initialization parameters are invalid or the cipher fails to initialize with the given parameters. */ public final void reset(int operation, Key key, int chaining) throws CryptoException { reset(operation, key, chaining, null, PADDING_NONE); } /** * Initializes this cipher in encryption or decryption mode, with the given chaining mode, initialization vector and without a padding. If this * algorithm requires an initialization vector and an invalid value was supplied, it will be generated using random values. Calling this method * will also reset the input data buffer. * * @param operation The operation mode of this cipher (<code>OPERATION_ENCRYPT</code> or <code>OPERATION_DECRYPT</code>). * @param key The key. * @param chaining The chaining mode of this cipher (<code>CHAINING_NONE</code>, <code>CHAINING_ECB</code>, or * <code>CHAINING_CBC</code>). * @param iv The initialization vector. * * @throws CryptoException if one or more initialization parameters are invalid or the cipher fails to initialize with the given parameters. */ public final void reset(int operation, Key key, int chaining, byte[] iv) throws CryptoException { reset(operation, key, chaining, iv, PADDING_NONE); } /** * Initializes this cipher in encryption or decryption mode, with the given chaining * mode, initialization vector padding. If this algorithm requires an initialization * vector and an invalid value was supplied, it will be generated using random values. * Calling this method will also reset the input data buffer. * * @param operation The operation mode of this cipher (<code>OPERATION_ENCRYPT</code> or <code>OPERATION_DECRYPT</code>). * @param key The key. * @param chaining The chaining mode of this cipher (<code>CHAINING_NONE</code>, <code>CHAINING_ECB</code>, or * <code>CHAINING_CBC</code>). * @param iv The initialization vector. * @param padding The padding mode of this cipher (<code>PADDING_NONE</code>, <code>PADDING_PKCS1</code>, or <code>PADDING_PKCS5</code>). * * @throws CryptoException if one or more initialization parameters are invalid or the cipher fails to initialize with the given parameters. */ public final void reset(int operation, Key key, int chaining, byte[] iv, int padding) throws CryptoException { if (operation < OPERATION_ENCRYPT || operation > OPERATION_DECRYPT) throw new CryptoException("Invalid or unsupported cipher operation: " + operation); if (key == null || !isKeySupported(key, operation)) throw new CryptoException("Invalid or unsupported cipher key: " + key); if (chaining < CHAINING_NONE || chaining > CHAINING_CBC || !isChainingSupported(chaining)) throw new CryptoException("Invalid or unsupported cipher chaining: " + chaining); if (padding < PADDING_NONE || padding > PADDING_PKCS5 || !isPaddingSupported(padding)) throw new CryptoException("Invalid or unsupported cipher padding: " + padding); if (operation == OPERATION_DECRYPT && chaining == CHAINING_CBC && iv == null) throw new CryptoException("The initialization vector must be specified in the CBC DECRYPT mode."); this.operation = operation; this.key = key; this.chaining = chaining; this.iv = iv; this.padding = padding; input.reset(); doReset(); } /** * Updates the input data that will be processed by this cipher algorithm. The data will be accumulated in an input buffer to be processed when * {@link #getOutput()} is finally called. * * @param data The input data. */ public final void update(int data) { oneByte[0] = (byte)(data & 0xFF); input.writeBytes(oneByte, 0, 1); } /** * Updates the input data that will be processed by this cipher algorithm. The data will be accumulated in an input buffer to be processed when * {@link #getOutput()} is finally called. * * @param data The input data. */ public final void update(byte[] data) { input.writeBytes(data, 0, data.length); } /** * Updates the input data that will be processed by this cipher algorithm. The data will be accumulated in an input buffer to be processed when * {@link #getOutput()} is finally called. * * @param data The input data. * @param start The offset in <code>data</code> where the data starts. * @param count The input length. */ public final void update(byte[] data, int start, int count) { input.writeBytes(data, start, count); } /** * Finalizes the encryption or decryption operation (depending on how this cipher was initialized) by processing all the accumulated input data * and returning the result in a new buffer. * * @return The operation result in a new buffer. */ public byte[] getOutput() throws CryptoException { if (operation != OPERATION_ENCRYPT && operation != OPERATION_DECRYPT) throw new CryptoException("Cipher is not initialized"); return process(input.toByteArray()); } /** * Returns the name of the algorithm. * * @return The name of the algorithm whose class heirs from Cipher. */ public abstract String getAlgorithm(); /** * Returns the block length. * * @return The block length (in bytes), or 0 if the underlying algorithm is not a block cipher. */ public abstract int getBlockLength(); protected abstract void doReset() throws NoSuchAlgorithmException, CryptoException; protected abstract byte[] process(byte[] data) throws CryptoException; protected abstract boolean isKeySupported(Key key, int operation); protected abstract boolean isChainingSupported(int chaining); protected abstract boolean isPaddingSupported(int padding); }