001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.midp.crypto;
028:
029: /**
030: * Class providing common functionality for DES ciphers.
031: */
032: abstract class BlockCipherBase extends Cipher {
033:
034: /** Block size. */
035: private int blockSize;
036:
037: /** ENCRYPT or DECRYPT. */
038: protected int mode;
039:
040: /** The padder. */
041: protected Padder padder;
042:
043: /** Contains the data that is not encrypted or decrypted yet. */
044: protected byte[] holdData;
045:
046: /** The holding buffer counter. */
047: protected int holdCount;
048:
049: /** Indicates if this cipher object has been updated or not. */
050: protected boolean isUpdated;
051:
052: /** Saved state variable. */
053: private byte[] savedHoldData;
054:
055: /** Saved state variable. */
056: private int savedHoldCount;
057:
058: /** Saved state variable. */
059: private boolean savedIsUpdated;
060:
061: /** Initial vector. */
062: protected byte[] IV;
063:
064: /** True in decryption with padder mode. */
065: private boolean keepLastBlock;
066:
067: /**
068: * Constructor.
069: *
070: * @param blockSize block size
071: */
072: protected BlockCipherBase(int blockSize) {
073: this .blockSize = blockSize;
074:
075: holdData = new byte[blockSize];
076: mode = MODE_UNINITIALIZED;
077: }
078:
079: /**
080: * Sets the padder.
081: *
082: * @param padding Upper case padding arg from the transformation given to
083: * Cipher.getInstance
084: *
085: * @exception NoSuchPaddingException if <code>padding</code>
086: * contains a padding scheme that is not available.
087: */
088: protected void setPadding(String padding)
089: throws NoSuchPaddingException {
090:
091: if (padding.equals("") || padding.equals("PKCS5PADDING")) {
092: padder = new PKCS5Padding(blockSize);
093: } else if (!padding.equals("NOPADDING")) {
094: throw new NoSuchPaddingException(padding);
095: }
096: }
097:
098: /**
099: * Initializes a cipher object with the key and sets
100: * encryption or decryption mode.
101: *
102: * @param mode either encryption or decription mode
103: * @param keyAlgorithm algorithm the key should have
104: * @param key key to be used
105: * @param needIV true if this algorithm accepts IV parameter
106: * @param params the algorithm parameters
107: *
108: * @exception InvalidKeyException if the given key
109: * is inappropriate for initializing this cipher
110: * @exception InvalidAlgorithmParameterException
111: * if the given algorithm parameters are inappropriate for this cipher
112: */
113: protected void doInit(int mode, String keyAlgorithm, Key key,
114: boolean needIV, CryptoParameter params)
115: throws InvalidKeyException,
116: InvalidAlgorithmParameterException {
117:
118: byte[] IV;
119:
120: if (needIV) {
121: if (params == null) {
122: if (mode == Cipher.DECRYPT_MODE) {
123: throw new InvalidAlgorithmParameterException();
124: }
125: IV = new byte[blockSize];
126: } else {
127: if (!(params instanceof IvParameter)) {
128: throw new InvalidAlgorithmParameterException();
129: }
130: IV = Util.cloneArray(((IvParameter) params).getIV());
131: if (IV.length != blockSize) {
132: throw new InvalidAlgorithmParameterException();
133: }
134: }
135: } else {
136: if (params != null) {
137: throw new InvalidAlgorithmParameterException();
138: }
139: IV = null;
140: }
141:
142: if (!(key instanceof SecretKey && keyAlgorithm.equals(key
143: .getAlgorithm()))) {
144: throw new InvalidKeyException();
145: }
146:
147: initKey(key.getEncoded(), mode);
148:
149: holdCount = 0;
150: isUpdated = false;
151: this .mode = mode;
152: this .IV = IV;
153: keepLastBlock = mode == Cipher.DECRYPT_MODE && padder != null;
154: }
155:
156: /**
157: * Encrypts or decrypts data in a single-part operation, or finishes a
158: * multiple-part operation. The data is encrypted or decrypted,
159: * depending on how this cipher was initialized.
160: *
161: * @param in the input buffer
162: * @param offset the offset in <code>input</code> where the input
163: * starts
164: * @param len the input length
165: * @param out the buffer for the result
166: * @param outOffset the offset in <code>output</code> where the result
167: * is stored
168: *
169: * @return the number of bytes stored in <code>output</code>
170: *
171: * @exception IllegalStateException if this cipher is in a wrong state
172: * (e.g., has not been initialized)
173: * @exception IllegalBlockSizeException if this cipher is a block cipher,
174: * no padding has been requested (only in encryption mode), and the total
175: * input length of the data processed by this cipher is not a multiple of
176: * block size
177: * @exception ShortBufferException if the given output buffer is too small
178: * to hold the result
179: * @exception BadPaddingException if this cipher is in decryption mode,
180: * and (un)padding has been requested, but the decrypted data is not
181: * bounded by the appropriate padding bytes
182: */
183: public int doFinal(byte in[], int offset, int len, byte out[],
184: int outOffset) throws IllegalStateException,
185: IllegalBlockSizeException, ShortBufferException,
186: BadPaddingException {
187:
188: Util.checkBounds(in, offset, len, out, outOffset);
189:
190: if (mode == MODE_UNINITIALIZED) {
191: throw new IllegalStateException();
192: }
193:
194: if (len == 0 && !isUpdated) {
195: return 0;
196: }
197:
198: boolean encrypt = mode == Cipher.ENCRYPT_MODE;
199:
200: // calculate the size of the possible out buffer
201: int expectedSize = len + holdCount;
202: int delta = expectedSize % blockSize;
203: if (delta != 0) {
204: if (!encrypt || (encrypt && padder == null)) {
205: throw new IllegalBlockSizeException();
206: }
207: expectedSize += blockSize - delta;
208: } else if (encrypt && padder != null) {
209: expectedSize += blockSize;
210: }
211:
212: int excess = outOffset + expectedSize - out.length;
213:
214: // padder may remove up to blockSize bytes after decryption
215: if (excess > (keepLastBlock ? blockSize : 0)) {
216: throw new ShortBufferException();
217: }
218:
219: if (keepLastBlock && excess > 0) {
220: // after unpadding data may not fit into output buffer
221: saveState();
222: }
223:
224: int counter = update(in, offset, len, out, outOffset);
225:
226: if (padder != null) {
227: if (encrypt) {
228: if (padder.pad(holdData, holdCount) != 0) {
229: processBlock(out, outOffset + counter);
230: counter += blockSize;
231: }
232: } else {
233: byte[] lastBlock = new byte[blockSize];
234: processBlock(lastBlock, 0);
235: int tail = blockSize
236: - padder.unPad(lastBlock, blockSize);
237: if (outOffset + counter + tail > out.length) {
238: restoreState();
239: throw new ShortBufferException();
240: }
241: System.arraycopy(lastBlock, 0, out,
242: outOffset + counter, tail);
243: counter += tail;
244: }
245: }
246:
247: holdCount = 0;
248: return counter;
249: }
250:
251: /**
252: * Continues a multiple-part encryption or decryption operation
253: * (depending on how this cipher was initialized), processing another data
254: * part.
255: * @param in the input buffer
256: * @param offset the offset in <code>input</code> where the input
257: * starts
258: * @param len the input length
259: * @param out the buffer for the result
260: * @param outOffset the offset in <code>output</code> where the result
261: * is stored
262: *
263: * @return the number of bytes stored in <code>output</code>
264: *
265: * @exception IllegalStateException if this cipher is in a wrong state
266: * (e.g., has not been initialized)
267: * @exception ShortBufferException if the given output buffer is too small
268: * to hold the result
269: */
270: public int update(byte in[], int offset, int len, byte out[],
271: int outOffset) throws IllegalStateException,
272: ShortBufferException {
273:
274: Util.checkBounds(in, offset, len, out, outOffset);
275:
276: if (mode == MODE_UNINITIALIZED) {
277: throw new IllegalStateException();
278: }
279:
280: if (len == 0) {
281: return 0;
282: }
283:
284: if (((holdCount + len) / blockSize - (keepLastBlock ? 1 : 0))
285: * blockSize > out.length - outOffset) {
286: throw new ShortBufferException();
287: }
288:
289: isUpdated = true;
290:
291: if (in == out) {
292: in = new byte[len];
293: System.arraycopy(out, offset, in, 0, len);
294: offset = 0;
295: }
296:
297: int counter = 0;
298: while (true) {
299:
300: int got;
301: System.arraycopy(in, offset, holdData, holdCount,
302: got = Math.min(blockSize - holdCount, len));
303: offset += got;
304: len -= got;
305: holdCount += got;
306:
307: if (holdCount < blockSize || (len == 0 && keepLastBlock)) {
308: return counter;
309: }
310:
311: processBlock(out, outOffset);
312:
313: counter += blockSize;
314: outOffset += blockSize;
315: }
316: }
317:
318: /**
319: * Returns the initialization vector (IV) in a new buffer.
320: * This is useful in the case where a random IV was created.
321: * @return the initialization vector in a new buffer,
322: * or <code>null</code> if the underlying algorithm does
323: * not use an IV.
324: */
325: public byte[] getIV() {
326: return IV == null ? null : Util.cloneArray(IV);
327: }
328:
329: /**
330: * Saves cipher state.
331: */
332: protected void saveState() {
333: savedHoldCount = holdCount;
334: savedHoldData = holdCount == 0 ? holdData : Util
335: .cloneArray(holdData);
336: savedIsUpdated = isUpdated;
337: }
338:
339: /**
340: * Restores cipher state.
341: */
342: protected void restoreState() {
343: holdCount = savedHoldCount;
344: holdData = savedHoldData;
345: isUpdated = savedIsUpdated;
346: }
347:
348: /**
349: * Depending on the mode, either encrypts or decrypts data block.
350: * @param out will contain the result of encryption
351: * or decryption operation
352: * @param offset is the offset in out
353: */
354: abstract void processBlock(byte[] out, int offset);
355:
356: /**
357: * Initializes key.
358: * @param data key data
359: * @param mode cipher mode
360: * @exception InvalidKeyException if the given key is inappropriate
361: * for this cipher
362: */
363: abstract void initKey(byte[] data, int mode)
364: throws InvalidKeyException;
365: }
|