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 */
017
018 package org.apache.geronimo.util.crypto.modes;
019
020 import org.apache.geronimo.util.crypto.BlockCipher;
021 import org.apache.geronimo.util.crypto.CipherParameters;
022 import org.apache.geronimo.util.crypto.DataLengthException;
023 import org.apache.geronimo.util.crypto.params.ParametersWithIV;
024
025 /**
026 * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher.
027 */
028 public class CBCBlockCipher
029 implements BlockCipher
030 {
031 private byte[] IV;
032 private byte[] cbcV;
033 private byte[] cbcNextV;
034
035 private int blockSize;
036 private BlockCipher cipher = null;
037 private boolean encrypting;
038
039 /**
040 * Basic constructor.
041 *
042 * @param cipher the block cipher to be used as the basis of chaining.
043 */
044 public CBCBlockCipher(
045 BlockCipher cipher)
046 {
047 this.cipher = cipher;
048 this.blockSize = cipher.getBlockSize();
049
050 this.IV = new byte[blockSize];
051 this.cbcV = new byte[blockSize];
052 this.cbcNextV = new byte[blockSize];
053 }
054
055 /**
056 * return the underlying block cipher that we are wrapping.
057 *
058 * @return the underlying block cipher that we are wrapping.
059 */
060 public BlockCipher getUnderlyingCipher()
061 {
062 return cipher;
063 }
064
065 /**
066 * Initialise the cipher and, possibly, the initialisation vector (IV).
067 * If an IV isn't passed as part of the parameter, the IV will be all zeros.
068 *
069 * @param encrypting if true the cipher is initialised for
070 * encryption, if false for decryption.
071 * @param params the key and other data required by the cipher.
072 * @exception IllegalArgumentException if the params argument is
073 * inappropriate.
074 */
075 public void init(
076 boolean encrypting,
077 CipherParameters params)
078 throws IllegalArgumentException
079 {
080 this.encrypting = encrypting;
081
082 if (params instanceof ParametersWithIV)
083 {
084 ParametersWithIV ivParam = (ParametersWithIV)params;
085 byte[] iv = ivParam.getIV();
086
087 if (iv.length != blockSize)
088 {
089 throw new IllegalArgumentException("initialisation vector must be the same length as block size");
090 }
091
092 System.arraycopy(iv, 0, IV, 0, iv.length);
093
094 reset();
095
096 cipher.init(encrypting, ivParam.getParameters());
097 }
098 else
099 {
100 reset();
101
102 cipher.init(encrypting, params);
103 }
104 }
105
106 /**
107 * return the algorithm name and mode.
108 *
109 * @return the name of the underlying algorithm followed by "/CBC".
110 */
111 public String getAlgorithmName()
112 {
113 return cipher.getAlgorithmName() + "/CBC";
114 }
115
116 /**
117 * return the block size of the underlying cipher.
118 *
119 * @return the block size of the underlying cipher.
120 */
121 public int getBlockSize()
122 {
123 return cipher.getBlockSize();
124 }
125
126 /**
127 * Process one block of input from the array in and write it to
128 * the out array.
129 *
130 * @param in the array containing the input data.
131 * @param inOff offset into the in array the data starts at.
132 * @param out the array the output data will be copied into.
133 * @param outOff the offset into the out array the output will start at.
134 * @exception DataLengthException if there isn't enough data in in, or
135 * space in out.
136 * @exception IllegalStateException if the cipher isn't initialised.
137 * @return the number of bytes processed and produced.
138 */
139 public int processBlock(
140 byte[] in,
141 int inOff,
142 byte[] out,
143 int outOff)
144 throws DataLengthException, IllegalStateException
145 {
146 return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff);
147 }
148
149 /**
150 * reset the chaining vector back to the IV and reset the underlying
151 * cipher.
152 */
153 public void reset()
154 {
155 System.arraycopy(IV, 0, cbcV, 0, IV.length);
156
157 cipher.reset();
158 }
159
160 /**
161 * Do the appropriate chaining step for CBC mode encryption.
162 *
163 * @param in the array containing the data to be encrypted.
164 * @param inOff offset into the in array the data starts at.
165 * @param out the array the encrypted data will be copied into.
166 * @param outOff the offset into the out array the output will start at.
167 * @exception DataLengthException if there isn't enough data in in, or
168 * space in out.
169 * @exception IllegalStateException if the cipher isn't initialised.
170 * @return the number of bytes processed and produced.
171 */
172 private int encryptBlock(
173 byte[] in,
174 int inOff,
175 byte[] out,
176 int outOff)
177 throws DataLengthException, IllegalStateException
178 {
179 if ((inOff + blockSize) > in.length)
180 {
181 throw new DataLengthException("input buffer too short");
182 }
183
184 /*
185 * XOR the cbcV and the input,
186 * then encrypt the cbcV
187 */
188 for (int i = 0; i < blockSize; i++)
189 {
190 cbcV[i] ^= in[inOff + i];
191 }
192
193 int length = cipher.processBlock(cbcV, 0, out, outOff);
194
195 /*
196 * copy ciphertext to cbcV
197 */
198 System.arraycopy(out, outOff, cbcV, 0, cbcV.length);
199
200 return length;
201 }
202
203 /**
204 * Do the appropriate chaining step for CBC mode decryption.
205 *
206 * @param in the array containing the data to be decrypted.
207 * @param inOff offset into the in array the data starts at.
208 * @param out the array the decrypted data will be copied into.
209 * @param outOff the offset into the out array the output will start at.
210 * @exception DataLengthException if there isn't enough data in in, or
211 * space in out.
212 * @exception IllegalStateException if the cipher isn't initialised.
213 * @return the number of bytes processed and produced.
214 */
215 private int decryptBlock(
216 byte[] in,
217 int inOff,
218 byte[] out,
219 int outOff)
220 throws DataLengthException, IllegalStateException
221 {
222 if ((inOff + blockSize) > in.length)
223 {
224 throw new DataLengthException("input buffer too short");
225 }
226
227 System.arraycopy(in, inOff, cbcNextV, 0, blockSize);
228
229 int length = cipher.processBlock(in, inOff, out, outOff);
230
231 /*
232 * XOR the cbcV and the output
233 */
234 for (int i = 0; i < blockSize; i++)
235 {
236 out[outOff + i] ^= cbcV[i];
237 }
238
239 /*
240 * swap the back up buffer into next position
241 */
242 byte[] tmp;
243
244 tmp = cbcV;
245 cbcV = cbcNextV;
246 cbcNextV = tmp;
247
248 return length;
249 }
250 }