Save This Page
Home » crypto-143 » org.bouncycastle.crypto » [javadoc | source]
    1   package org.bouncycastle.crypto;
    2   
    3   
    4   /**
    5    * A wrapper class that allows block ciphers to be used to process data in
    6    * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the
    7    * buffer is full and more data is being added, or on a doFinal.
    8    * <p>
    9    * Note: in the case where the underlying cipher is either a CFB cipher or an
   10    * OFB one the last block may not be a multiple of the block size.
   11    */
   12   public class BufferedBlockCipher
   13   {
   14       protected byte[]        buf;
   15       protected int           bufOff;
   16   
   17       protected boolean       forEncryption;
   18       protected BlockCipher   cipher;
   19   
   20       protected boolean       partialBlockOkay;
   21       protected boolean       pgpCFB;
   22   
   23       /**
   24        * constructor for subclasses
   25        */
   26       protected BufferedBlockCipher()
   27       {
   28       }
   29   
   30       /**
   31        * Create a buffered block cipher without padding.
   32        *
   33        * @param cipher the underlying block cipher this buffering object wraps.
   34        */
   35       public BufferedBlockCipher(
   36           BlockCipher     cipher)
   37       {
   38           this.cipher = cipher;
   39   
   40           buf = new byte[cipher.getBlockSize()];
   41           bufOff = 0;
   42   
   43           //
   44           // check if we can handle partial blocks on doFinal.
   45           //
   46           String  name = cipher.getAlgorithmName();
   47           int     idx = name.indexOf('/') + 1;
   48   
   49           pgpCFB = (idx > 0 && name.startsWith("PGP", idx));
   50   
   51           if (pgpCFB)
   52           {
   53               partialBlockOkay = true;
   54           }
   55           else
   56           {
   57               partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx)));
   58           }
   59       }
   60   
   61       /**
   62        * return the cipher this object wraps.
   63        *
   64        * @return the cipher this object wraps.
   65        */
   66       public BlockCipher getUnderlyingCipher()
   67       {
   68           return cipher;
   69       }
   70   
   71       /**
   72        * initialise the cipher.
   73        *
   74        * @param forEncryption if true the cipher is initialised for
   75        *  encryption, if false for decryption.
   76        * @param params the key and other data required by the cipher.
   77        * @exception IllegalArgumentException if the params argument is
   78        * inappropriate.
   79        */
   80       public void init(
   81           boolean             forEncryption,
   82           CipherParameters    params)
   83           throws IllegalArgumentException
   84       {
   85           this.forEncryption = forEncryption;
   86   
   87           reset();
   88   
   89           cipher.init(forEncryption, params);
   90       }
   91   
   92       /**
   93        * return the blocksize for the underlying cipher.
   94        *
   95        * @return the blocksize for the underlying cipher.
   96        */
   97       public int getBlockSize()
   98       {
   99           return cipher.getBlockSize();
  100       }
  101   
  102       /**
  103        * return the size of the output buffer required for an update 
  104        * an input of len bytes.
  105        *
  106        * @param len the length of the input.
  107        * @return the space required to accommodate a call to update
  108        * with len bytes of input.
  109        */
  110       public int getUpdateOutputSize(
  111           int len)
  112       {
  113           int total       = len + bufOff;
  114           int leftOver;
  115   
  116           if (pgpCFB)
  117           {
  118               leftOver    = total % buf.length - (cipher.getBlockSize() + 2);
  119           }
  120           else
  121           {
  122               leftOver    = total % buf.length;
  123           }
  124   
  125           return total - leftOver;
  126       }
  127   
  128       /**
  129        * return the size of the output buffer required for an update plus a
  130        * doFinal with an input of len bytes.
  131        *
  132        * @param len the length of the input.
  133        * @return the space required to accommodate a call to update and doFinal
  134        * with len bytes of input.
  135        */
  136       public int getOutputSize(
  137           int len)
  138       {
  139           int total       = len + bufOff;
  140           int leftOver;
  141   
  142           if (pgpCFB)
  143           {
  144               leftOver    = total % buf.length - (cipher.getBlockSize() + 2);
  145           }
  146           else
  147           {
  148               leftOver    = total % buf.length;
  149               if (leftOver == 0)
  150               {
  151                   return total;
  152               }
  153           }
  154   
  155           return total - leftOver + buf.length;
  156       }
  157   
  158       /**
  159        * process a single byte, producing an output block if neccessary.
  160        *
  161        * @param in the input byte.
  162        * @param out the space for any output that might be produced.
  163        * @param outOff the offset from which the output will be copied.
  164        * @return the number of output bytes copied to out.
  165        * @exception DataLengthException if there isn't enough space in out.
  166        * @exception IllegalStateException if the cipher isn't initialised.
  167        */
  168       public int processByte(
  169           byte        in,
  170           byte[]      out,
  171           int         outOff)
  172           throws DataLengthException, IllegalStateException
  173       {
  174           int         resultLen = 0;
  175   
  176           buf[bufOff++] = in;
  177   
  178           if (bufOff == buf.length)
  179           {
  180               resultLen = cipher.processBlock(buf, 0, out, outOff);
  181               bufOff = 0;
  182           }
  183   
  184           return resultLen;
  185       }
  186   
  187       /**
  188        * process an array of bytes, producing output if necessary.
  189        *
  190        * @param in the input byte array.
  191        * @param inOff the offset at which the input data starts.
  192        * @param len the number of bytes to be copied out of the input array.
  193        * @param out the space for any output that might be produced.
  194        * @param outOff the offset from which the output will be copied.
  195        * @return the number of output bytes copied to out.
  196        * @exception DataLengthException if there isn't enough space in out.
  197        * @exception IllegalStateException if the cipher isn't initialised.
  198        */
  199       public int processBytes(
  200           byte[]      in,
  201           int         inOff,
  202           int         len,
  203           byte[]      out,
  204           int         outOff)
  205           throws DataLengthException, IllegalStateException
  206       {
  207           if (len < 0)
  208           {
  209               throw new IllegalArgumentException("Can't have a negative input length!");
  210           }
  211   
  212           int blockSize   = getBlockSize();
  213           int length      = getUpdateOutputSize(len);
  214           
  215           if (length > 0)
  216           {
  217               if ((outOff + length) > out.length)
  218               {
  219                   throw new DataLengthException("output buffer too short");
  220               }
  221           }
  222   
  223           int resultLen = 0;
  224           int gapLen = buf.length - bufOff;
  225   
  226           if (len > gapLen)
  227           {
  228               System.arraycopy(in, inOff, buf, bufOff, gapLen);
  229   
  230               resultLen += cipher.processBlock(buf, 0, out, outOff);
  231   
  232               bufOff = 0;
  233               len -= gapLen;
  234               inOff += gapLen;
  235   
  236               while (len > buf.length)
  237               {
  238                   resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen);
  239   
  240                   len -= blockSize;
  241                   inOff += blockSize;
  242               }
  243           }
  244   
  245           System.arraycopy(in, inOff, buf, bufOff, len);
  246   
  247           bufOff += len;
  248   
  249           if (bufOff == buf.length)
  250           {
  251               resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
  252               bufOff = 0;
  253           }
  254   
  255           return resultLen;
  256       }
  257   
  258       /**
  259        * Process the last block in the buffer.
  260        *
  261        * @param out the array the block currently being held is copied into.
  262        * @param outOff the offset at which the copying starts.
  263        * @return the number of output bytes copied to out.
  264        * @exception DataLengthException if there is insufficient space in out for
  265        * the output, or the input is not block size aligned and should be.
  266        * @exception IllegalStateException if the underlying cipher is not
  267        * initialised.
  268        * @exception InvalidCipherTextException if padding is expected and not found.
  269        * @exception DataLengthException if the input is not block size
  270        * aligned.
  271        */
  272       public int doFinal(
  273           byte[]  out,
  274           int     outOff)
  275           throws DataLengthException, IllegalStateException, InvalidCipherTextException
  276       {
  277           int resultLen = 0;
  278   
  279           if (outOff + bufOff > out.length)
  280           {
  281               throw new DataLengthException("output buffer too short for doFinal()");
  282           }
  283   
  284           if (bufOff != 0 && partialBlockOkay)
  285           {
  286               cipher.processBlock(buf, 0, buf, 0);
  287               resultLen = bufOff;
  288               bufOff = 0;
  289               System.arraycopy(buf, 0, out, outOff, resultLen);
  290           }
  291           else if (bufOff != 0)
  292           {
  293               throw new DataLengthException("data not block size aligned");
  294           }
  295   
  296           reset();
  297   
  298           return resultLen;
  299       }
  300   
  301       /**
  302        * Reset the buffer and cipher. After resetting the object is in the same
  303        * state as it was after the last init (if there was one).
  304        */
  305       public void reset()
  306       {
  307           //
  308           // clean the buffer.
  309           //
  310           for (int i = 0; i < buf.length; i++)
  311           {
  312               buf[i] = 0;
  313           }
  314   
  315           bufOff = 0;
  316   
  317           //
  318           // reset the underlying cipher.
  319           //
  320           cipher.reset();
  321       }
  322   }

Save This Page
Home » crypto-143 » org.bouncycastle.crypto » [javadoc | source]