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