001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.geronimo.crypto.crypto.modes;
017:
018: import org.apache.geronimo.crypto.crypto.BlockCipher;
019: import org.apache.geronimo.crypto.crypto.CipherParameters;
020: import org.apache.geronimo.crypto.crypto.DataLengthException;
021: import org.apache.geronimo.crypto.crypto.params.ParametersWithIV;
022:
023: /**
024: * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
025: */
026: public class CBCBlockCipher implements BlockCipher {
027: private byte[] IV;
028: private byte[] cbcV;
029: private byte[] cbcNextV;
030:
031: private int blockSize;
032: private BlockCipher cipher = null;
033: private boolean encrypting;
034:
035: /**
036: * Basic constructor.
037: *
038: * @param cipher the block cipher to be used as the basis of chaining.
039: */
040: public CBCBlockCipher(BlockCipher cipher) {
041: this .cipher = cipher;
042: this .blockSize = cipher.getBlockSize();
043:
044: this .IV = new byte[blockSize];
045: this .cbcV = new byte[blockSize];
046: this .cbcNextV = new byte[blockSize];
047: }
048:
049: /**
050: * return the underlying block cipher that we are wrapping.
051: *
052: * @return the underlying block cipher that we are wrapping.
053: */
054: public BlockCipher getUnderlyingCipher() {
055: return cipher;
056: }
057:
058: /**
059: * Initialise the cipher and, possibly, the initialisation vector (IV).
060: * If an IV isn't passed as part of the parameter, the IV will be all zeros.
061: *
062: * @param encrypting if true the cipher is initialised for
063: * encryption, if false for decryption.
064: * @param params the key and other data required by the cipher.
065: * @exception IllegalArgumentException if the params argument is
066: * inappropriate.
067: */
068: public void init(boolean encrypting, CipherParameters params)
069: throws IllegalArgumentException {
070: this .encrypting = encrypting;
071:
072: if (params instanceof ParametersWithIV) {
073: ParametersWithIV ivParam = (ParametersWithIV) params;
074: byte[] iv = ivParam.getIV();
075:
076: if (iv.length != blockSize) {
077: throw new IllegalArgumentException(
078: "initialisation vector must be the same length as block size");
079: }
080:
081: System.arraycopy(iv, 0, IV, 0, iv.length);
082:
083: reset();
084:
085: cipher.init(encrypting, ivParam.getParameters());
086: } else {
087: reset();
088:
089: cipher.init(encrypting, params);
090: }
091: }
092:
093: /**
094: * return the algorithm name and mode.
095: *
096: * @return the name of the underlying algorithm followed by "/CBC".
097: */
098: public String getAlgorithmName() {
099: return cipher.getAlgorithmName() + "/CBC";
100: }
101:
102: /**
103: * return the block size of the underlying cipher.
104: *
105: * @return the block size of the underlying cipher.
106: */
107: public int getBlockSize() {
108: return cipher.getBlockSize();
109: }
110:
111: /**
112: * Process one block of input from the array in and write it to
113: * the out array.
114: *
115: * @param in the array containing the input data.
116: * @param inOff offset into the in array the data starts at.
117: * @param out the array the output data will be copied into.
118: * @param outOff the offset into the out array the output will start at.
119: * @exception DataLengthException if there isn't enough data in in, or
120: * space in out.
121: * @exception IllegalStateException if the cipher isn't initialised.
122: * @return the number of bytes processed and produced.
123: */
124: public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
125: throws DataLengthException, IllegalStateException {
126: return (encrypting) ? encryptBlock(in, inOff, out, outOff)
127: : decryptBlock(in, inOff, out, outOff);
128: }
129:
130: /**
131: * reset the chaining vector back to the IV and reset the underlying
132: * cipher.
133: */
134: public void reset() {
135: System.arraycopy(IV, 0, cbcV, 0, IV.length);
136:
137: cipher.reset();
138: }
139:
140: /**
141: * Do the appropriate chaining step for CBC mode encryption.
142: *
143: * @param in the array containing the data to be encrypted.
144: * @param inOff offset into the in array the data starts at.
145: * @param out the array the encrypted data will be copied into.
146: * @param outOff the offset into the out array the output will start at.
147: * @exception DataLengthException if there isn't enough data in in, or
148: * space in out.
149: * @exception IllegalStateException if the cipher isn't initialised.
150: * @return the number of bytes processed and produced.
151: */
152: private int encryptBlock(byte[] in, int inOff, byte[] out,
153: int outOff) throws DataLengthException,
154: IllegalStateException {
155: if ((inOff + blockSize) > in.length) {
156: throw new DataLengthException("input buffer too short");
157: }
158:
159: /*
160: * XOR the cbcV and the input,
161: * then encrypt the cbcV
162: */
163: for (int i = 0; i < blockSize; i++) {
164: cbcV[i] ^= in[inOff + i];
165: }
166:
167: int length = cipher.processBlock(cbcV, 0, out, outOff);
168:
169: /*
170: * copy ciphertext to cbcV
171: */
172: System.arraycopy(out, outOff, cbcV, 0, cbcV.length);
173:
174: return length;
175: }
176:
177: /**
178: * Do the appropriate chaining step for CBC mode decryption.
179: *
180: * @param in the array containing the data to be decrypted.
181: * @param inOff offset into the in array the data starts at.
182: * @param out the array the decrypted data will be copied into.
183: * @param outOff the offset into the out array the output will start at.
184: * @exception DataLengthException if there isn't enough data in in, or
185: * space in out.
186: * @exception IllegalStateException if the cipher isn't initialised.
187: * @return the number of bytes processed and produced.
188: */
189: private int decryptBlock(byte[] in, int inOff, byte[] out,
190: int outOff) throws DataLengthException,
191: IllegalStateException {
192: if ((inOff + blockSize) > in.length) {
193: throw new DataLengthException("input buffer too short");
194: }
195:
196: System.arraycopy(in, inOff, cbcNextV, 0, blockSize);
197:
198: int length = cipher.processBlock(in, inOff, out, outOff);
199:
200: /*
201: * XOR the cbcV and the output
202: */
203: for (int i = 0; i < blockSize; i++) {
204: out[outOff + i] ^= cbcV[i];
205: }
206:
207: /*
208: * swap the back up buffer into next position
209: */
210: byte[] tmp;
211:
212: tmp = cbcV;
213: cbcV = cbcNextV;
214: cbcNextV = tmp;
215:
216: return length;
217: }
218: }
|