/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program 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. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.midp.crypto;
/**
* Class providing common functionality for DES ciphers.
*/
abstract class BlockCipherBase extends Cipher {
/** Block size. */
private int blockSize;
/** ENCRYPT or DECRYPT. */
protected int mode;
/** The padder. */
protected Padder padder;
/** Contains the data that is not encrypted or decrypted yet. */
protected byte[] holdData;
/** The holding buffer counter. */
protected int holdCount;
/** Indicates if this cipher object has been updated or not. */
protected boolean isUpdated;
/** Saved state variable. */
private byte[] savedHoldData;
/** Saved state variable. */
private int savedHoldCount;
/** Saved state variable. */
private boolean savedIsUpdated;
/** Initial vector. */
protected byte[] IV;
/** True in decryption with padder mode. */
private boolean keepLastBlock;
/**
* Constructor.
*
* @param blockSize block size
*/
protected BlockCipherBase(int blockSize) {
this.blockSize = blockSize;
holdData = new byte[blockSize];
mode = MODE_UNINITIALIZED;
}
/**
* Sets the padder.
*
* @param padding Upper case padding arg from the transformation given to
* Cipher.getInstance
*
* @exception NoSuchPaddingException if <code>padding</code>
* contains a padding scheme that is not available.
*/
protected void setPadding(String padding)
throws NoSuchPaddingException {
if (padding.equals("") || padding.equals("PKCS5PADDING")) {
padder = new PKCS5Padding(blockSize);
} else if (!padding.equals("NOPADDING")) {
throw new NoSuchPaddingException(padding);
}
}
/**
* Initializes a cipher object with the key and sets
* encryption or decryption mode.
*
* @param mode either encryption or decription mode
* @param keyAlgorithm algorithm the key should have
* @param key key to be used
* @param needIV true if this algorithm accepts IV parameter
* @param params the algorithm parameters
*
* @exception InvalidKeyException if the given key
* is inappropriate for initializing this cipher
* @exception InvalidAlgorithmParameterException
* if the given algorithm parameters are inappropriate for this cipher
*/
protected void doInit(int mode, String keyAlgorithm, Key key,
boolean needIV, CryptoParameter params)
throws InvalidKeyException, InvalidAlgorithmParameterException {
byte[] IV;
if (needIV) {
if (params == null) {
if (mode == Cipher.DECRYPT_MODE) {
throw new InvalidAlgorithmParameterException();
}
IV = new byte[blockSize];
} else {
if (! (params instanceof IvParameter)) {
throw new InvalidAlgorithmParameterException();
}
IV = Util.cloneArray(((IvParameter)params).getIV());
if (IV.length != blockSize) {
throw new InvalidAlgorithmParameterException();
}
}
} else {
if (params != null) {
throw new InvalidAlgorithmParameterException();
}
IV = null;
}
if (!(key instanceof SecretKey &&
keyAlgorithm.equals(key.getAlgorithm()))) {
throw new InvalidKeyException();
}
initKey(key.getEncoded(), mode);
holdCount = 0;
isUpdated = false;
this.mode = mode;
this.IV = IV;
keepLastBlock = mode == Cipher.DECRYPT_MODE && padder != null;
}
/**
* Encrypts or decrypts data in a single-part operation, or finishes a
* multiple-part operation. The data is encrypted or decrypted,
* depending on how this cipher was initialized.
*
* @param in the input buffer
* @param offset the offset in <code>input</code> where the input
* starts
* @param len the input length
* @param out the buffer for the result
* @param outOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized)
* @exception IllegalBlockSizeException if this cipher is a block cipher,
* no padding has been requested (only in encryption mode), and the total
* input length of the data processed by this cipher is not a multiple of
* block size
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
* @exception BadPaddingException if this cipher is in decryption mode,
* and (un)padding has been requested, but the decrypted data is not
* bounded by the appropriate padding bytes
*/
public int doFinal(byte in[], int offset, int len,
byte out[], int outOffset)
throws IllegalStateException, IllegalBlockSizeException,
ShortBufferException, BadPaddingException {
Util.checkBounds(in, offset, len, out, outOffset);
if (mode == MODE_UNINITIALIZED) {
throw new IllegalStateException();
}
if (len == 0 && ! isUpdated) {
return 0;
}
boolean encrypt = mode == Cipher.ENCRYPT_MODE;
// calculate the size of the possible out buffer
int expectedSize = len + holdCount;
int delta = expectedSize % blockSize;
if (delta != 0) {
if (! encrypt || (encrypt && padder == null)) {
throw new IllegalBlockSizeException();
}
expectedSize += blockSize - delta;
} else
if (encrypt && padder != null) {
expectedSize += blockSize;
}
int excess = outOffset + expectedSize - out.length;
// padder may remove up to blockSize bytes after decryption
if (excess > (keepLastBlock ? blockSize : 0)) {
throw new ShortBufferException();
}
if (keepLastBlock && excess > 0) {
// after unpadding data may not fit into output buffer
saveState();
}
int counter = update(in, offset, len, out, outOffset);
if (padder != null) {
if (encrypt) {
if (padder.pad(holdData, holdCount) != 0) {
processBlock(out, outOffset + counter);
counter += blockSize;
}
} else {
byte[] lastBlock = new byte[blockSize];
processBlock(lastBlock, 0);
int tail = blockSize - padder.unPad(lastBlock, blockSize);
if (outOffset + counter + tail > out.length) {
restoreState();
throw new ShortBufferException();
}
System.arraycopy(lastBlock, 0,
out, outOffset + counter, tail);
counter += tail;
}
}
holdCount = 0;
return counter;
}
/**
* Continues a multiple-part encryption or decryption operation
* (depending on how this cipher was initialized), processing another data
* part.
* @param in the input buffer
* @param offset the offset in <code>input</code> where the input
* starts
* @param len the input length
* @param out the buffer for the result
* @param outOffset the offset in <code>output</code> where the result
* is stored
*
* @return the number of bytes stored in <code>output</code>
*
* @exception IllegalStateException if this cipher is in a wrong state
* (e.g., has not been initialized)
* @exception ShortBufferException if the given output buffer is too small
* to hold the result
*/
public int update(byte in[], int offset, int len,
byte out[], int outOffset)
throws IllegalStateException, ShortBufferException {
Util.checkBounds(in, offset, len, out, outOffset);
if (mode == MODE_UNINITIALIZED) {
throw new IllegalStateException();
}
if (len == 0) {
return 0;
}
if (((holdCount + len) / blockSize -
(keepLastBlock ? 1 : 0)) * blockSize >
out.length - outOffset) {
throw new ShortBufferException();
}
isUpdated = true;
if (in == out) {
in = new byte[len];
System.arraycopy(out, offset, in, 0, len);
offset = 0;
}
int counter = 0;
while (true) {
int got;
System.arraycopy(in, offset, holdData, holdCount,
got = Math.min(blockSize - holdCount, len));
offset += got;
len -= got;
holdCount += got;
if (holdCount < blockSize || (len == 0 && keepLastBlock)) {
return counter;
}
processBlock(out, outOffset);
counter += blockSize;
outOffset += blockSize;
}
}
/**
* 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.
*/
public byte[] getIV() {
return IV == null ? null : Util.cloneArray(IV);
}
/**
* Saves cipher state.
*/
protected void saveState() {
savedHoldCount = holdCount;
savedHoldData = holdCount == 0 ?
holdData :
Util.cloneArray(holdData);
savedIsUpdated = isUpdated;
}
/**
* Restores cipher state.
*/
protected void restoreState() {
holdCount = savedHoldCount;
holdData = savedHoldData;
isUpdated = savedIsUpdated;
}
/**
* Depending on the mode, either encrypts or decrypts data block.
* @param out will contain the result of encryption
* or decryption operation
* @param offset is the offset in out
*/
abstract void processBlock(byte[] out, int offset);
/**
* Initializes key.
* @param data key data
* @param mode cipher mode
* @exception InvalidKeyException if the given key is inappropriate
* for this cipher
*/
abstract void initKey(byte[] data, int mode) throws InvalidKeyException;
}
|