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 }