1   /* $RCSfile: OperationModeCBC.java,v $
2    * $Revision: 1.11 $
3    * $Date: 2002/11/23 11:09:56 $
4    * $Author: uwe_guenther $
5    * $State: Exp $
6    *
7    * Created on August 16, 2001 11:36 AM
8    *
9    * Copyright (C) 2001 Uwe Guenther <uwe@cscc.de>
10   *
11   * This file is part of the jhbci JCE-ServiceProvider. The jhbci JCE-
12   * ServiceProvider is a library, written in JavaTM, that should be 
13   * used in HBCI banking applications (clients and may be servers),
14   * to do cryptographic operations.
15   *
16   * The jhbci library is free software; you can redistribute it and/or
17   * modify it under the terms of the GNU Lesser General Public
18   * License as published by the Free Software Foundation; either
19   * version 2.1 of the License, or (at your option) any later version.
20   *
21   * The jhbci library is distributed in the hope that it will be useful,
22   * but WITHOUT ANY WARRANTY; without even the implied warranty of
23   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24   * Lesser General Public License for more details.
25   *
26   * You should have received a copy of the GNU Lesser General Public
27   * License along with this library; if not, write to the Free Software
28   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29   *
30   */
31  
32  package de.cscc.crypto.provider;
33  
34  import javax.crypto.IllegalBlockSizeException;
35  import javax.crypto.ShortBufferException;
36  
37  import de.cscc.crypto.util.ByteUtil;
38  
39  /** 
40   * OperationModeCBC Class.
41   *
42   * @author  <a href=mailto:uwe@cscc.de>Uwe G&uuml;nther</a>
43   * @version $Revision: 1.11 $
44   */
45  class OperationModeCBC extends OperationMode {   
46      
47      /** 
48       * A BlockCipher object that will be used for this OperationMode 
49       * algorithm.
50       */    
51      private BlockCipher cipher;    
52      
53      /** 
54       * The initialization vector where we will start the
55       * cipher block chaining.
56       */    
57      private byte[] iv;
58      
59      /** The current valid chaining vector that will be used. */    
60      private byte[] chainingVector;     
61      
62      /** Don't create a new OperationModeCBC with the default constructor. */
63      private OperationModeCBC() {}
64      
65      /**
66       * Creates new OperationModeCBC with a BlockCipher.
67       * Default iv is 0x000000 ... 000 with length == getBlockSize().
68       *
69       * @param cipher the cipher to wrap.
70       */
71      public OperationModeCBC(BlockCipher cipher) {
72          this.cipher = cipher;
73          
74          //set default iv
75          this.iv = new byte[getBlockSize()];
76          this.chainingVector = new byte[getBlockSize()];        
77      }
78  
79      /**
80       * Returns a string representation of the object. 
81       *
82       * @return  a string representation of the object.
83       */
84      public String toString() {
85          String returnValue = "";
86          returnValue += "Cipher: [" + this.cipher + "] ";        
87          returnValue += "IV: [" + ByteUtil.toHex(this.iv) + "] ";
88          returnValue += "Chaining Vector: [null] "; 
89          returnValue += "Chaining Vector: [" + 
90                  ByteUtil.toHex(this.chainingVector) + "]";
91          return returnValue;
92      }
93          
94      /** 
95       * Set an initialization vector (iv) as a deep copy.
96       * The iv have to getBlockSize() bytes long.
97       *
98       * @param iv the initialization vector (iv).
99       * @throws IllegalBlockSizeException if the iv is smaller than 8 bytes.
100      * If the longer than 8 bytes, only the first 8 bytes are used.
101      */    
102     public void setIv(byte[] iv) throws IllegalBlockSizeException {
103         if (iv.length < getBlockSize()){
104             throw new IllegalBlockSizeException(
105                 "Initialization Vector is " + iv.length + 
106                 " bytes long, but it should be " +  
107                 getBlockSize()  + ".");
108         } 
109 
110         System.arraycopy(iv, 0, this.iv, 0, getBlockSize());
111         
112         //If we set the Iv we will also set the chaining vector to the Iv,
113         //because we start aevery time with the Iv, if the Iv is new set.
114         setChainingVector(this.iv, 0);
115     }
116     
117     /** 
118      * Returns a deep copy of the internal byte[] array,
119      * which contains the initialization vector (iv),
120      * or null of the mode does not need an iv.
121      * Such as ECB.
122      *
123      * @return a deep copy of the internal byte[] array,
124      * which contains the initialization vector (iv),
125      * or null of the mode does not need an iv.
126      */    
127     public byte[] getIv() {
128         /*byte[] returnValue = new byte[getBlockSize()];
129         System.arraycopy(this.iv, 0, returnValue, 0, getBlockSize());
130         return returnValue;*/
131         return (byte[]) this.iv.clone();
132     }
133     
134     /** Set chaning vector to the initial iv value. */
135     public void resetToIv() {
136         try {
137             setChainingVector(this.iv, 0);
138         } catch (IllegalBlockSizeException cannothappen) {
139             //Will never be thrown because this.iv.length == getBlockSize().
140             throw new Error("Can not happen.", cannothappen);
141         }
142     }    
143     
144     /** 
145      * Set the current valid chaining vector from a
146      * byte[offset+8] array.
147      * @param input the input buffer.
148      * @param offset the offset in input where the input starts.
149      * @throws IllegalBlockSizeException will be thrown, if the
150      *    deliverd byte array has a length lesser than 8. 
151      */    
152     private void setChainingVector(byte[] input, int offset)
153             throws IllegalBlockSizeException {
154         if (input.length-offset < getBlockSize()){
155             throw new IllegalBlockSizeException(
156                 "Initialization Vector is " + (input.length-offset) + 
157                 " bytes long, but it should be " +  getBlockSize()  + ".");
158         } 
159                 
160         System.arraycopy(input, offset, 
161                          this.chainingVector, 0, 
162                          getBlockSize());
163     }
164 
165     /** 
166      * Returns the algorithm specific block size in bytes. These block
167      * size will be processed if one encrypt or decrypt method runs.
168      * If you subclass these abstract class, you have to implement these
169      * method.
170      *
171      * @return the algorithm specific block size in bytes.
172      */
173     public int getBlockSize() {
174         return this.cipher.getBlockSize();
175     }
176     
177     /**
178      * The encryption operation.
179      *
180      * If (input.length-inputOffset > 8) we use only the first 8 bytes
181      * for the block.
182      *
183      * @param input the 64 bit data block that will be encrypted.
184      * @param inputOffset the offset in input where the input starts.
185      * @param output the buffer for the result.
186      * @param outputOffset the offset in output where the result is stored.
187      * @throws IllegalBlockSizeException will be thrown, if the
188      *        deliverd byte array has a length lesser than 8.
189      * @throws ShortBufferException will be thrown, if the
190      *        output buffer byte array has a length lesser than 8.
191      * @return the number of bytes stored in output.
192      */
193     public int encrypt(byte[] input, int inputOffset, 
194             byte[] output, int outputOffset)
195             throws IllegalBlockSizeException, ShortBufferException {
196         
197         // we check only the input buffer, because the output buffer
198         // will be checked in this.cipher.encrypt(...)
199         if (input.length-inputOffset < getBlockSize()){
200             throw new IllegalBlockSizeException(
201                 "The input block range is " + (input.length-inputOffset) + 
202                 " bytes long, but it should be " +  getBlockSize()  + 
203                 " or longer.");
204         }             
205       
206         //xor chaning vector with input block
207         for (int i =0; i < getBlockSize(); i++){
208             this.chainingVector[i] ^= input[inputOffset+i];        
209         }
210         
211         //Xor the chaining Vector to input block.
212         //Throws IllegalBlockSizeException and/or ShortBufferException             
213         int returnValue = this.cipher.encrypt(this.chainingVector, 0,
214                                               output, outputOffset);        
215         
216         //Set the new encryptd cipher text block as new chaining vector
217         //for the next encrypt() invokation.        
218         setChainingVector(output, outputOffset);        
219         
220         return returnValue;
221     }
222     
223     /** 
224      * The DES decryption operation.
225      *
226      * If (input.length-inputOffset > 8) we use only the first 8 bytes
227      * for the block.
228      *
229      * @param input the 64 bit data block that will be decrypted.
230      * @param inputOffset the offset in input where the input starts.
231      * @param output the buffer for the result.
232      * @param outputOffset the offset in output where the result is stored.
233      * @throws IllegalBlockSizeException will be thrown, if the
234      *         deliverd byte array has a length lesser than getBlockSize().
235      * @throws ShortBufferException will be thrown, if the
236      *         output buffer byte array has a length lesser than getBlockSize().
237      * @return the number of bytes stored in output.
238      */
239     public int decrypt(byte[] input, int inputOffset, 
240                        byte[] output, int outputOffset)
241             throws IllegalBlockSizeException, ShortBufferException {        
242         //Decrypt the cipher text block.
243         int returnValue = this.cipher.decrypt(input, inputOffset, 
244                                               output, outputOffset);
245 
246         //Xor the plaintext block with the chaining vector.
247         //Throws IllegalBlockSizeException and/or ShortBufferException
248         for (int i = 0; i < getBlockSize(); i++){
249             output[outputOffset+i] ^= this.chainingVector[i];
250         }                
251         
252         //Set the chaining vector for the next decrypt round
253         //to the encrypted input for this decrypt round.
254         setChainingVector(input, inputOffset);
255         
256         //Returns the block lentgh in byte, usually 8 byte.
257         return returnValue;
258     }
259 }
260