1   /* $RCSfile: PaddingNoPadding.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 19, 2001 4:29 PM
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  /**
38   * PaddingNoPadding Class.
39   *
40   * @author  <a href=mailto:uwe@cscc.de>Uwe G&uuml;nther</a>
41   * @version $Revision: 1.11 $
42   */
43  class PaddingNoPadding extends Padding {
44      
45      /** 
46       * The operation mode object (such as ECB, or CBC), that
47       * will be wrapped from this  padding object.
48       */    
49      private OperationMode mode;
50      
51      /**
52       * A single buffer to store unprocessed plaintext,
53       * while processing a block cipher.
54       */    
55      private PaddingSingleBuffer unprocessedPlainTextBuffer;
56      
57      /**
58       * A single buffer to store unprocessed ciphertext,
59       * while processing a block cipher.
60       */    
61      private PaddingSingleBuffer unprocessedCipherTextBuffer;    
62      
63      /**
64       * Temporary buffer that will be used from al en- and decrypt methods
65       * to prvent exceive byt[] allocation in these methods.
66       */
67      private byte[] tempBuffer;
68      
69      
70      /** Don't create an PaddingNoPadding with the default constructor */
71      private PaddingNoPadding() {}
72      
73      /** 
74       * Constructs a new padding object, that wraps a operation mode
75       * object.
76       *
77       * @param mode the operation mode algorithm object that we wrap.
78       */    
79      public PaddingNoPadding(OperationMode mode){
80          
81          this.mode = mode;
82          this.unprocessedPlainTextBuffer = 
83                  new PaddingSingleBuffer(getBlockSize());
84          this.unprocessedCipherTextBuffer = 
85                  new PaddingSingleBuffer(getBlockSize());      
86          
87          this.tempBuffer = new byte[getBlockSize()];        
88      }
89      
90      /**
91       * Returns a string representation of the object. 
92       *
93       * @return  a string representation of the object.
94       */
95      public String toString() {
96          
97          String returnValue = "";
98  
99          returnValue += "[mode: " + this.mode + "] ";
100         returnValue += "[unprocessedPlainTextBuffer: " + this.unprocessedPlainTextBuffer + "] ";
101         returnValue += "[unprocessedCipherTextBuffer: " + this.unprocessedCipherTextBuffer + "] ";
102         
103         return returnValue;
104     }
105     
106     /**
107      * Get the block size of the underlying mode object.
108      *
109      * @return the block size of the underlying mode object.
110      */    
111     public int getBlockSize() {
112         return mode.getBlockSize();
113     }
114 
115     /** 
116      * Calculates the output size for a returned byted array, or
117      * the size for the output array that will
118      * be passed through updateEncryption(.....).
119      *
120      * @param inputLength the input length, that will be base for the
121      * calculation.
122      * @return the size for a returned byted array, or
123      * the size for the output array that will
124      * be passed through updateEncryption(.....).
125      */    
126     public int getUpdateEncryptionOutputSize(int inputLength) {        
127         //Skip the rest if we divide with getBlockSize()
128         return (
129                     (
130                         this.unprocessedPlainTextBuffer.hasBytes() + inputLength
131                     ) / getBlockSize()
132                ) * getBlockSize();
133     }    
134     
135     /** 
136      * Continues a multipart encryption operation, processing
137      * another data part.
138      *
139      * @param input plainText that should be encrypted.
140      * @param inputOffset the offset in input where the input starts.
141      * @param inputLength the input length.
142      * @throws IllegalArgumentException if <tt>input == null</tt>, 
143      * <tt>inputOffset &lt; 0</tt>, <tt>inputLength < 0</tt> or 
144      * <tt>input.length &lt; inputOffset+inputLength</tt>.
145      * @return the encrypted data, if there enough (at least one block size),
146      * or byte[0]  otherwise. We aren't allowed to return null, like javadoc
147      * for CipherSpi this says, because the JCE 
148      * (byte[] Cipher#update(byte[], int, int)) will throw a
149      * NullPointerException.
150      */    
151     public byte[] updateEncryption(byte[] input, int inputOffset, 
152             int inputLength) {
153         //The next three if's are hte same as the JCE does check,
154         //but these checks are not documented, so we do check again.
155         if (input == null) {
156             throw new IllegalArgumentException("Bad Arguments.");
157         }
158         
159         if (inputOffset  < 0                         || 
160             inputLength  < 0                         || 
161             input.length < (inputOffset+inputLength)) { 
162             
163             throw new IllegalArgumentException("Bad Arguments.");
164         }
165         
166         ////if (inputLength == 0){
167         ////    return null;
168         ////}
169         
170         
171         byte[] output =null;
172         int outputOffset =0;
173         
174         //Will be there some result, get space for the result output byte array.
175         ////if(getUpdateEncryptionOutputSize(inputLength) > 0){
176         output = new byte[getUpdateEncryptionOutputSize(inputLength)];
177         ////}        
178         
179         //Process the whole input.
180         for (int i = inputOffset; i < inputOffset+inputLength; i++) {
181             
182             //Add every single byte to our unprocessedPlainTextBuffer.
183             this.unprocessedPlainTextBuffer.add(input[i]);
184             
185             //If our unprocessedPlainTextBuffer is full process it and put 
186             //the result into output.
187             if (this.unprocessedPlainTextBuffer.isFull()) {
188                             
189                 //Process the unprocessedPlainTextBuffer and switch the 
190                 //outputOffset one block further.
191                 try {  
192                     this.unprocessedPlainTextBuffer.fetch(this.tempBuffer, 0);
193                     
194                     outputOffset += 
195                     this.mode.encrypt(this.tempBuffer, 0, output, outputOffset);
196                 } catch (IllegalBlockSizeException cannothappen){
197                     throw new Error("Can not happen.", cannothappen);
198                 } catch (ShortBufferException cannothappen){
199                     throw new Error("Can not happen.", cannothappen);
200                 }
201             }
202         }
203                 
204         //Return the result as byte array, or byte[0] if there no result.
205         return output;
206     }
207     
208     /** 
209      * Continues a multipart encryption operation, processing
210      * another data part.
211      *
212      * @param input plainText that should be encrypted.
213      * @param inputOffset the offset in input where the input starts.
214      * @param inputLength the input length.
215      * @param output the buffer for the encrypted result.
216      * @param outputOffset the offset in output where the result is stored.
217      * @throws ShortBufferException if the usable range in the output buffer is 
218      * to less. This means that the usable range must at least
219      * getUpdateEncryptionOutputSize(inputLength) bytes large.
220      * @throws IllegalArgumentException if <tt>input == null</tt>, 
221      * <tt>inputOffset &lt; 0</tt>, <tt>inputLength < 0</tt>, 
222      * <tt>outputOffset &lt; 0</tt> or <tt>input.length &lt; 
223      * inputOffset+inputLength</tt>.
224      * @return the number of bytes stored in output.
225      */    
226     public int updateEncryption(byte[] input, int inputOffset, int inputLength,
227             byte[] output, int outputOffset) throws ShortBufferException {          
228         //The next three if's are the same as the JCE does check,
229         //but these checks are not documented, so we do check again.
230         if (input == null) {
231             throw new IllegalArgumentException("Bad Arguments.");
232         }
233         
234         if (inputOffset  < 0                         || 
235             inputLength  < 0                         || 
236             outputOffset < 0                         || 
237             input.length < (inputOffset+inputLength)) {
238             
239             throw new IllegalArgumentException("Bad Arguments.");
240         }
241         
242         if (inputLength == 0) {
243             return 0;
244         }  
245         
246         
247         //Check output for null.
248         if (output == null) {
249             throw new ShortBufferException(
250                 "Output buffer should be (at least) " + 
251                 getUpdateEncryptionOutputSize(inputLength) + 
252                 " bytes or larger, but not null.");               
253         }
254             
255         //Check, if output is large enough.
256         if (getUpdateEncryptionOutputSize(inputLength) > output.length-outputOffset) {
257             throw new ShortBufferException(
258                 "Usable byte range in output buffer is " + 
259                 (output.length-outputOffset) + " bytes large, but it should be " + 
260                 getUpdateEncryptionOutputSize(inputLength) + " bytes or larger.");               
261         }
262         
263         
264         //Save outputOffset, to calc number of bytes stored in output.
265         int saveOutputOffset =outputOffset;
266         
267         //Process the whole input.
268         for (int i = inputOffset; i < inputOffset+inputLength; i++) {
269             
270             //Add every single byte to our unprocessedPlainTextBuffer.
271             this.unprocessedPlainTextBuffer.add(input[i]);
272             
273             //If our unprocessedPlainTextBuffer is full process it and put the
274             //result into output.
275             if (this.unprocessedPlainTextBuffer.isFull()) {
276                 
277                 //If one of the Exception, declared in the catch clause, was 
278                 //thrown, the cause is orginated here, so we catch it here.                
279                 try {  
280                     this.unprocessedPlainTextBuffer.fetch(this.tempBuffer, 0);
281                     
282                     //Process our unprocessedPlainTextBuffer and add the number 
283                     //of new output bytes to outputOffset.                    
284                     outputOffset += 
285                     this.mode.encrypt(this.tempBuffer, 0, output, outputOffset);
286                 } catch (IllegalBlockSizeException cannothappen) {
287                     throw new Error("Can not happen.", cannothappen);
288                 } catch (ShortBufferException cannothappen) {
289                     throw new Error("Can not happen.", cannothappen);
290                 }
291             }
292         }
293         
294         //Return the number of bytes stored in output.
295         return outputOffset - saveOutputOffset;
296     }
297     
298     /** 
299      * Calculates the output size for a returned byted array, or
300      * the size for the output array that will
301      * be passed through doFinalEncryption(.....).
302      *
303      * @param inputLength the input length, that will be base for the
304      * calculation.
305      * @return the size for a returned byted array, or
306      * the size for the output array that will
307      * be passed through doFinalEncryption(.....).
308      */    
309     public int getDoFinalEncryptionOutputSize(int inputLength) {
310         //getUpdateSize() + one block for the padding bytes.
311         return getUpdateEncryptionOutputSize(inputLength);
312     }
313     
314     /**
315      * Checks if the result from buffer bytes and
316      * a given inputLength is a multiple of the
317      * block size of the underlying mode object.
318      *
319      * @param inputLength the input length, that will be base for the
320      * verification.
321      * @return true if the result from buffer bytes and
322      * a given inputLength is a multiple of the
323      * block size of the underlying mode object,
324      * false otherwise.
325      */    
326     private boolean isPlainTextBufferAndInputLengthMultipleOfBlockSize(
327             int inputLength) {
328         return (
329                     this.unprocessedPlainTextBuffer.hasBytes() + inputLength
330                ) % getBlockSize() == 0;
331     }        
332     
333     /** 
334      * Encrypts data in a single part operation, or finishes
335      * a multipart encryption.
336      *
337      * @param input plainText that should be encrypted.
338      * @param inputOffset the offset in input where the input starts.
339      * @param inputLength the input length.
340      * @throws IllegalBlockSizeException If all processed bytes in a multipart decryption
341      * are not multiple of operation mode block size.
342      * @throws IllegalArgumentException if <tt>input == null</tt>, 
343      * <tt>inputOffset &lt; 0</tt>, <tt>inputLength < 0</tt>, 
344      * or <tt>input.length &lt; inputOffset+inputLength</tt>.
345      * @return the padded and encrypted data.
346      */    
347     public byte[] doFinalEncryption(byte[] input, int inputOffset, 
348             int inputLength) throws IllegalBlockSizeException {
349         //The next three if's are hte same as the JCE does check,
350         //but these checks are not documented, so we do check again.
351         if (input == null && inputOffset == 0 && inputLength == 0) {
352             //At these point the JCE byte[] doFinal() was invoked.
353         } else {
354             
355             if (input == null) {
356                 throw new IllegalArgumentException("Bad Arguments.");
357             }
358         
359             if (inputOffset  < 0                         || 
360                 inputLength  < 0                         || 
361                 input.length < (inputOffset+inputLength)) { 
362             
363                 throw new IllegalArgumentException("Bad Arguments.");
364             }
365         }
366         
367         if (isPlainTextBufferAndInputLengthMultipleOfBlockSize(inputLength) == false) {
368             throw new IllegalBlockSizeException(
369                     "Input length not multiple of " + getBlockSize() + " bytes.");
370         }        
371 
372         //Get space for the result output byte array.
373         byte[] output = new byte[getDoFinalEncryptionOutputSize(inputLength)];
374         int outputOffset =0;
375         
376         //Process the whole input.
377         for (int i = inputOffset; i < inputOffset+inputLength; i++) {
378             
379             //Add every single byte to our unprocessedPlainTextBuffer.
380             this.unprocessedPlainTextBuffer.add(input[i]);
381             
382             //If our unprocessedPlainTextBuffer is full process it and put
383             //the result into output.
384             if (this.unprocessedPlainTextBuffer.isFull()) {
385 
386                 //If one of the Exception, declared in the catch clause, was 
387                 //thrown, the cause is orginated here, so we catch it here.                
388                 try {
389                     this.unprocessedPlainTextBuffer.fetch(this.tempBuffer, 0);
390                     
391                     //Process our unprocessedPlainTextBuffer and add the number 
392                     //of new output bytes to outputOffset.
393                     outputOffset += 
394                     this.mode.encrypt(this.tempBuffer, 0, output, outputOffset);
395                 } catch (IllegalBlockSizeException cannothappen) {
396                     throw new Error("Can not happen.", cannothappen);
397                 } catch (ShortBufferException cannothappen) {
398                     throw new Error("Can not happen.", cannothappen);
399                 }
400             }
401         }
402 
403         //Now all things are done, so we can reset the mode object to reuse it.
404         this.mode.resetToIv();        
405         
406         //Return the result as byte array, or null if there no result.
407         return output;
408     }
409 
410     /** 
411      * Encrypts data in a single part operation, or finishes
412      * a multipart encryption.
413      *
414      * @param input plainText that should be encrypted.
415      * @param inputOffset the offset in input where the input starts.
416      * @param inputLength the input length.
417      * @param output the buffer for the padded and encrypted result.
418      * @param outputOffset the offset in output where the result is stored.
419      * @throws IllegalBlockSizeException If all processed bytes in a multipart decryption
420      * are not multiple of operation mode block size.
421      * @throws ShortBufferException if the usable range in the output buffer is
422      * to less. This means that the usable range must at least
423      * getDoFinalEncryptionOutputSize(inputLength) bytes large.
424      * @throws IllegalArgumentException if <tt>input == null</tt>, 
425      * <tt>inputOffset &lt; 0</tt>, <tt>inputLength < 0</tt>, 
426      * <tt>outputOffset &lt; 0</tt> or <tt>input.length &lt; 
427      * inputOffset+inputLength</tt>.
428      * @return the number of bytes stored in output.
429      */    
430     public int doFinalEncryption(byte[] input, int inputOffset, int inputLength,
431             byte[] output, int outputOffset)
432             throws IllegalBlockSizeException, ShortBufferException {        
433         //The next three if's are hte same as the JCE does check,
434         //but these checks are not documented, so we do check again.
435         if (input == null && inputOffset == 0 && inputLength == 0) {
436             //At these point the JCE 
437             //byte[] doFinal(byte[] output, int outputOffset) was invoked.
438         } else {
439             
440             if (input == null) {
441                 throw new IllegalArgumentException("Bad Arguments.");
442             }
443         
444             if (inputOffset  < 0                         || 
445                 inputLength  < 0                         || 
446                 outputOffset < 0                         || 
447                 input.length < (inputOffset+inputLength)) {
448             
449                 throw new IllegalArgumentException("Bad Arguments.");
450             }
451         }
452         
453         if (isPlainTextBufferAndInputLengthMultipleOfBlockSize(inputLength) == false) {
454             throw new IllegalBlockSizeException(
455                     "Input length not multiple of " + getBlockSize() + " bytes.");
456         }        
457         
458         //Check output for null.
459         if (output == null) {
460             throw new ShortBufferException(
461                 "Output buffer should be (at least) " + 
462                 getDoFinalEncryptionOutputSize(inputLength) + 
463                 " bytes or larger, but not null.");               
464         }
465             
466         //Check, if output is large enough.
467         if (getUpdateEncryptionOutputSize(inputLength) > output.length-outputOffset) {
468             throw new ShortBufferException(
469                 "Usable byte range in output buffer is " + 
470                 (output.length-outputOffset) + " bytes large, but it should be " + 
471                 getDoFinalEncryptionOutputSize(inputLength) + " bytes or larger.");               
472         }        
473         
474         
475         //Save outputOffset, to calc number of bytes stored in output.
476         int saveOutputOffset = outputOffset;
477         
478         //Process the whole input.
479         for (int i = inputOffset; i < inputOffset+inputLength; i++) {
480             
481             //Add every single byte to our unprocessedPlainTextBuffer.
482             this.unprocessedPlainTextBuffer.add(input[i]);
483             
484             //If our unprocessedPlainTextBuffer is full process it and put the 
485             //result into output.
486             if (this.unprocessedPlainTextBuffer.isFull()) {
487 
488                 //If one of the Exception, declared in the catch clause, was 
489                 //thrown, the cause is orginated here, so we catch it here.                
490                 try {
491                     this.unprocessedPlainTextBuffer.fetch(this.tempBuffer, 0);
492                     
493                     //Process our unprocessedPlainTextBuffer and add the number 
494                     //of new output bytes to outputOffset.
495                     outputOffset += 
496                     this.mode.encrypt(this.tempBuffer, 0, output, outputOffset);
497                 } catch (IllegalBlockSizeException cannothappen){
498                     throw new Error("Can not happen.", cannothappen);
499                 } 
500             }
501         }
502 
503         //Now all things are done, so we can reset the mode object to reuse it.
504         this.mode.resetToIv();
505         
506         //Return the number of bytes stored in output.
507         return outputOffset - saveOutputOffset;
508     }
509     
510     /** 
511      * Calculates the output size for a returned byted array, or
512      * the size for the output array that will
513      * be passed through updateDecryption(.....).
514      *
515      * @param inputLength the input length, that will be base for the
516      * calculation.
517      * @return the size for a returned byted array, or
518      * the size for the output array that will
519      * be passed through updateDecryption(.....).
520      */    
521     public int getUpdateDecryptionOutputSize(int inputLength) {                
522         //Skip the rest if we divide with getBlockSize()
523         return (
524                     (
525                         this.unprocessedCipherTextBuffer.hasBytes() + inputLength
526                     ) / getBlockSize()
527                ) * getBlockSize();     
528     }    
529     
530     /** 
531      * Continues a multipart decryption operation, processing
532      * another data part.
533      *
534      * @param input padded cipherText that should be decrypted.
535      * @param inputOffset the offset in input where the input starts.
536      * @param inputLength the input length.
537      * @return the decrypted data, if there enough (at least one block size + 
538      * one byte), or new byte[0] otherwise. We aren't allowed to return null, 
539      * like javadoc for CipherSpi this says, because the JCE 
540      * (byte[] Cipher#update(byte[], int, int)) will throw a
541      * NullPointerException.
542      * @throws IllegalArgumentException if <tt>input == null</tt>, 
543      * <tt>inputOffset &lt; 0</tt>, <tt>inputLength < 0</tt>, 
544      * or <tt>input.length &lt; 
545      * inputOffset+inputLength</tt>.
546      */    
547     public byte[] updateDecryption(byte[] input, int inputOffset, 
548             int inputLength) {
549         //The next three if's are the same as the JCE does check,
550         //but these checks are not documented, so we do check again.
551         if (input == null) {
552             throw new IllegalArgumentException("Bad Arguments.");
553         }
554         
555         if (inputOffset  < 0                         || 
556             inputLength  < 0                         || 
557             input.length < (inputOffset+inputLength)) { 
558             
559             throw new IllegalArgumentException("Bad Arguments.");
560         }
561         
562         ////if (inputLength == 0){
563         ////    return null;
564         ////}
565         
566         
567         byte[] output = null;
568         int outputOffset = 0;
569         
570         //Will be there some result, get space for the result output byte array.
571         ////if(getUpdateDecryptionOutputSize(inputLength) > 0){
572         output = new byte[getUpdateDecryptionOutputSize(inputLength)];
573         ////}        
574 
575         //Process the whole input.
576         for (int i = inputOffset; i < inputOffset+inputLength; i++) {
577             
578             //Add every single byte to our unprocessedCipherTextBuffer.
579             this.unprocessedCipherTextBuffer.add(input[i]);
580             
581             //If our unprocessedCipherTextBuffer is full, process it 
582             //and put the result into output.
583             if (this.unprocessedCipherTextBuffer.isFull()) {
584                 
585                 //If one of the Exception, declared in the catch clause, was 
586                 //thrown, the cause is orginated here, so we catch it here.                
587                 try {  
588                     this.unprocessedCipherTextBuffer.fetch(this.tempBuffer, 0);
589                     
590                     //Process our unprocessedCipherTextBuffer and add the number
591                     //of new output bytes to outputOffset.
592                     outputOffset += 
593                     this.mode.decrypt(this.tempBuffer, 0, output, outputOffset);
594                 } catch (IllegalBlockSizeException cannothappen) {
595                     throw new Error("Can not happen.", cannothappen);
596                 } catch (ShortBufferException cannothappen) {
597                     throw new Error("Can not happen.", cannothappen);
598                 }
599             }
600         }
601         
602         //Return the result as byte array, or byte[0] if there no result.
603         return output;
604     }
605     
606     /** 
607      * Continues a multipart decryption operation, processing
608      * another data part.
609      *
610      * @param input padded cipherText that should be decrypted.
611      * @param inputOffset the offset in input where the input starts.
612      * @param inputLength the input length.
613      * @param output the buffer for the decrypted result.
614      * @param outputOffset the offset in output where the result is stored.
615      * @throws ShortBufferException if the usable range in the output buffer is 
616      * to less. This means that the usable range must at least
617      * getUpdateDecryptionOutputSize(inputLength) bytes large.
618      * @throws IllegalArgumentException if <tt>input == null</tt>, 
619      * <tt>inputOffset &lt; 0</tt>, <tt>inputLength < 0</tt>, 
620      * <tt>outputOffset &lt; 0</tt> or <tt>input.length &lt; 
621      * inputOffset+inputLength</tt>.
622      * @return the number of bytes stored in output.
623      */    
624     public int updateDecryption(byte[] input, int inputOffset, int inputLength,
625             byte[] output, int outputOffset) throws ShortBufferException {  
626         //The next three if's are hte same as the JCE does check,
627         //but these checks are not documented, so we do check again.
628         if (input == null) {
629             throw new IllegalArgumentException("Bad Arguments.");
630         }
631         
632         if (inputOffset  < 0                         || 
633             inputLength  < 0                         || 
634             outputOffset < 0                         || 
635             input.length < (inputOffset+inputLength)) {
636             
637             throw new IllegalArgumentException("Bad Arguments.");
638         }
639         
640         if (inputLength == 0) {
641             return 0;
642         }  
643         
644         
645         //Check output for null.
646         if (output == null) {
647             throw new ShortBufferException(
648                 "Output buffer should be (at least) " + 
649                 getUpdateDecryptionOutputSize(inputLength) + 
650                 " bytes or larger, but not null.");               
651         }
652             
653         //Check, if output is large enough.
654         if (getUpdateDecryptionOutputSize(inputLength) > output.length-outputOffset){
655             throw new ShortBufferException(
656                 "Usable byte range in output buffer is " + 
657                 (output.length-outputOffset) + " bytes large, but it should be " + 
658                 getUpdateDecryptionOutputSize(inputLength) + " bytes or larger.");               
659         }
660         
661         
662         //Save outputOffset, to calc number of bytes stored in output.
663         int saveOutputOffset =outputOffset;
664         
665         //Process the whole input.
666         for (int i = inputOffset; i < inputOffset+inputLength; i++) {
667             
668             //Add every single byte to our unprocessedCipherTextBuffer.
669             this.unprocessedCipherTextBuffer.add(input[i]);
670             
671             //If our unprocessedCipherTextBuffer is full, process it 
672             //and put the result into output.
673             if (this.unprocessedCipherTextBuffer.isFull()) {
674                 
675                 //If one of the Exception, declared in the catch clause, was 
676                 //thrown, the cause is orginated here, so we catch it here.
677                 try {  
678                     this.unprocessedCipherTextBuffer.fetch(this.tempBuffer, 0);
679                     
680                     //Process our unprocessedCipherTextBuffer and add the number 
681                     //of new output bytes to outputOffset.
682                     outputOffset += 
683                     this.mode.decrypt(this.tempBuffer, 0, output, outputOffset);
684                 } catch (IllegalBlockSizeException cannothappen) {
685                     throw new Error("Can not happen.", cannothappen);
686                 } catch (ShortBufferException cannothappen) {
687                     throw new Error("Can not happen.", cannothappen);
688                 }
689             }
690         }
691         
692         //Return the number of bytes stored in output.
693         return outputOffset - saveOutputOffset;
694     }
695 
696     /** 
697      * Calculates the output size for a returned byted array, or
698      * the size for the output array that will
699      * be passed through doFinalDecryption(.....).
700      *
701      * @param inputLength the input length, that will be base for the
702      * calculation.
703      * @return the size for a returned byted array, or
704      * the size for the output array that will
705      * be passed through doFinalDecryption(.....).
706      */    
707     public int getDoFinalDecryptionOutputSize(int inputLength) {
708         //Skip the rest if we divide with getBlockSize()
709         return (
710                     (
711                         this.unprocessedCipherTextBuffer.hasBytes() + inputLength
712                     ) / getBlockSize()
713                ) * getBlockSize();     
714     }    
715     
716     /** Checks if the result from buffer bytes and
717      * a given inputLength is a multiple of the
718      * block size of the underlying mode object.
719      * @param inputLength the input length, that will be base for the
720      * verification.
721      * @return true if the result from buffer bytes and
722      * a given inputLength is a multiple of the
723      * block size of the underlying mode object,
724      * false otherwise.
725      */    
726     private boolean isCipherTextBufferAndInputLengthMultipleOfBlockSize(
727             int inputLength) {
728         return (
729                     this.unprocessedCipherTextBuffer.hasBytes() + inputLength
730                ) % getBlockSize() == 0;
731     }
732     
733     /** 
734      * Decrypts data in a single part operation, or finishes
735      * a multipart decryption.
736      * @param input padded cipherText that should be decrypted.
737      * @param inputOffset the offset in input where the input starts.
738      * @param inputLength the input length.
739      * @throws IllegalBlockSizeException if the final size of processed bytes 
740      * not a multiple of block size.
741      * @return the unpadded and decrypted data.
742      */    
743     public byte[] doFinalDecryption(byte[] input, int inputOffset, 
744             int inputLength) throws IllegalBlockSizeException {
745         //The next three if's are the same as the JCE does check,
746         //but these checks are not documented, so we do check again.
747         if (input == null && inputOffset == 0 && inputLength == 0) {
748             //At these point the JCE byte[] doFinal() was invoked.
749         } else {
750             
751             if (input == null) {
752                 throw new IllegalArgumentException("Bad Arguments.");
753             }
754         
755             if (inputOffset  < 0                         || 
756                 inputLength  < 0                         || 
757                 input.length < (inputOffset+inputLength)) { 
758             
759                 throw new IllegalArgumentException("Bad Arguments.");
760             }
761         }
762 
763         if (isCipherTextBufferAndInputLengthMultipleOfBlockSize(inputLength) == false){
764             throw new IllegalBlockSizeException("Input length not multiple of " 
765                     + getBlockSize() + " bytes.");
766         }
767         
768         byte[] output = new byte[getDoFinalDecryptionOutputSize(inputLength)];
769         int outputOffset =0;
770         
771         //Process the whole input.
772         for (int i = inputOffset; i < inputOffset+inputLength; i++) {
773             
774             //Add every single byte to our unprocessedCipherTextBuffer.
775             this.unprocessedCipherTextBuffer.add(input[i]);
776             
777             //If our unprocessedCipherTextBuffer has enough bytes, process it 
778             //and put the result into output.
779             if (this.unprocessedCipherTextBuffer.isFull()) {
780 
781                 //If one of the Exception, declared in the catch clause, was 
782                 //thrown, the cause is orginated here, so we catch it here.                
783                 try {
784                     this.unprocessedCipherTextBuffer.fetch(this.tempBuffer, 0);
785                     
786                     //Process our unprocessedCipherTextBuffer and add the number
787                     //of new output bytes to outputOffset.
788                     outputOffset += 
789                     this.mode.decrypt(this.tempBuffer, 0, output, outputOffset);
790                 } catch (ShortBufferException cannothappen){
791                     throw new Error("Can not happen.", cannothappen);
792                 }
793             }
794         }
795         
796         //At these point we reset the mode Iv, to leave the Object in a valid 
797         //state (for further use).
798         this.mode.resetToIv();        
799     
800         //Return the result as byte array.
801         return output;
802     }    
803     
804     /** 
805      * Decrypts data in a single part operation, or finishes
806      * a multipart decryption.
807      *
808      * @param input padded cipherText that should be decrypted.
809      * @param inputOffset the offset in input where the input starts.
810      * @param inputLength the input length.
811      * @param output the buffer for the unpadded and decrypted result.
812      * @param outputOffset the offset in output where the result is stored.
813      * @throws IllegalBlockSizeException if the final size of processed bytes 
814      * not a multiple of block size.
815      * @throws ShortBufferException if the usable range in the output buffer is 
816      * to less. This means that the usable range must at least
817      * getDoFinalDecryptionOutputSize(inputLength) bytes large.
818      * @throws IllegalArgumentException if <tt>input == null</tt>, 
819      * <tt>inputOffset &lt; 0</tt>, <tt>inputLength < 0</tt>, 
820      * <tt>outputOffset &lt; 0</tt> or <tt>input.length &lt; 
821      * inputOffset+inputLength</tt>.
822      * @return the number of bytes stored in output.
823      */    
824     public int doFinalDecryption(byte[] input, int inputOffset, int inputLength,
825             byte[] output, int outputOffset) 
826             throws IllegalBlockSizeException, ShortBufferException {
827         //The next three if's are the same as the JCE does check,
828         //but these checks are not documented, so we do check again.
829         if (input == null && inputOffset == 0 && inputLength == 0) {
830             //At these point the JCE byte[] doFinal() was invoked.
831         } else {
832             
833             if (input == null) {
834                 throw new IllegalArgumentException("Bad Arguments.");
835             }
836         
837             if (inputOffset  < 0                         || 
838                 inputLength  < 0                         || 
839                 input.length < (inputOffset+inputLength) ) { 
840             
841                 throw new IllegalArgumentException("Bad Arguments.");
842             }
843         }
844 
845         if (isCipherTextBufferAndInputLengthMultipleOfBlockSize(inputLength) == false) {
846             throw new IllegalBlockSizeException("Input length not multiple of " 
847                     + getBlockSize() + " bytes.");
848         }
849         
850         int saveOutputOffset = outputOffset;        
851         
852         //Process the whole input.
853         for (int i = inputOffset; i < inputOffset+inputLength; i++){
854             
855             //Add every single byte to our unprocessedCipherTextBuffer.
856             this.unprocessedCipherTextBuffer.add(input[i]);
857             
858             //If our unprocessedCipherTextBuffer has enough bytes, process it 
859             //and put the result into output.
860             if (this.unprocessedCipherTextBuffer.isFull()){
861 
862                 this.unprocessedCipherTextBuffer.fetch(this.tempBuffer, 0);    
863                 
864                 //Process our unprocessedCipherTextBuffer and add the number
865                 //of new output bytes to outputOffset.
866                 outputOffset += 
867                 this.mode.decrypt(this.tempBuffer, 0, output, outputOffset);
868              }
869         }
870         
871         //At these point we reset the mode Iv, to leave the Object in a valid 
872         //state (for further use).
873         this.mode.resetToIv();        
874         
875         //Return the number of bytes stored in output.
876         return outputOffset-saveOutputOffset;
877     }        
878 }
879