1 /*
   2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.crypto;
  27 
  28 import java.io.*;
  29 
  30 /**
  31  * A CipherInputStream is composed of an InputStream and a Cipher so
  32  * that read() methods return data that are read in from the
  33  * underlying InputStream but have been additionally processed by the
  34  * Cipher.  The Cipher must be fully initialized before being used by
  35  * a CipherInputStream.
  36  *
  37  * <p> For example, if the Cipher is initialized for decryption, the
  38  * CipherInputStream will attempt to read in data and decrypt them,
  39  * before returning the decrypted data.
  40  *
  41  * <p> This class adheres strictly to the semantics, especially the
  42  * failure semantics, of its ancestor classes
  43  * java.io.FilterInputStream and java.io.InputStream.  This class has
  44  * exactly those methods specified in its ancestor classes, and
  45  * overrides them all.  Moreover, this class catches all exceptions
  46  * that are not thrown by its ancestor classes.  In particular, the
  47  * <code>skip</code> method skips, and the <code>available</code>
  48  * method counts only data that have been processed by the encapsulated Cipher.
  49  *
  50  * <p> It is crucial for a programmer using this class not to use
  51  * methods that are not defined or overriden in this class (such as a
  52  * new method or constructor that is later added to one of the super
  53  * classes), because the design and implementation of those methods
  54  * are unlikely to have considered security impact with regard to
  55  * CipherInputStream.
  56  *
  57  * @author  Li Gong
  58  * @see     java.io.InputStream
  59  * @see     java.io.FilterInputStream
  60  * @see     javax.crypto.Cipher
  61  * @see     javax.crypto.CipherOutputStream
  62  *
  63  * @since 1.4
  64  */
  65 
  66 public class CipherInputStream extends FilterInputStream {
  67 
  68     // the cipher engine to use to process stream data
  69     private Cipher cipher;
  70 
  71     // the underlying input stream
  72     private InputStream input;
  73 
  74     /* the buffer holding data that have been read in from the
  75        underlying stream, but have not been processed by the cipher
  76        engine. the size 512 bytes is somewhat randomly chosen */
  77     private byte[] ibuffer = new byte[512];
  78 
  79     // having reached the end of the underlying input stream
  80     private boolean done = false;
  81 
  82     /* the buffer holding data that have been processed by the cipher
  83        engine, but have not been read out */
  84     private byte[] obuffer;
  85     // the offset pointing to the next "new" byte
  86     private int ostart = 0;
  87     // the offset pointing to the last "new" byte
  88     private int ofinish = 0;
  89     // stream status
  90     private boolean closed = false;
  91 
  92     /**
  93      * private convenience function.
  94      *
  95      * Entry condition: ostart = ofinish
  96      *
  97      * Exit condition: ostart <= ofinish
  98      *
  99      * return (ofinish-ostart) (we have this many bytes for you)
 100      * return 0 (no data now, but could have more later)
 101      * return -1 (absolutely no more data)
 102      */
 103     private int getMoreData() throws IOException {
 104         if (done) return -1;
 105         int readin = input.read(ibuffer);
 106         if (readin == -1) {
 107             done = true;
 108             try {
 109                 obuffer = cipher.doFinal();
 110             }
 111             catch (IllegalBlockSizeException e) {obuffer = null;}
 112             catch (BadPaddingException e) {obuffer = null;}
 113             if (obuffer == null)
 114                 return -1;
 115             else {
 116                 ostart = 0;
 117                 ofinish = obuffer.length;
 118                 return ofinish;
 119             }
 120         }
 121         try {
 122             obuffer = cipher.update(ibuffer, 0, readin);
 123         } catch (IllegalStateException e) {obuffer = null;};
 124         ostart = 0;
 125         if (obuffer == null)
 126             ofinish = 0;
 127         else ofinish = obuffer.length;
 128         return ofinish;
 129     }
 130 
 131     /**
 132      * Constructs a CipherInputStream from an InputStream and a
 133      * Cipher.
 134      * <br>Note: if the specified input stream or cipher is
 135      * null, a NullPointerException may be thrown later when
 136      * they are used.
 137      * @param is the to-be-processed input stream
 138      * @param c an initialized Cipher object
 139      */
 140     public CipherInputStream(InputStream is, Cipher c) {
 141         super(is);
 142         input = is;
 143         cipher = c;
 144     }
 145 
 146     /**
 147      * Constructs a CipherInputStream from an InputStream without
 148      * specifying a Cipher. This has the effect of constructing a
 149      * CipherInputStream using a NullCipher.
 150      * <br>Note: if the specified input stream is null, a
 151      * NullPointerException may be thrown later when it is used.
 152      * @param is the to-be-processed input stream
 153      */
 154     protected CipherInputStream(InputStream is) {
 155         super(is);
 156         input = is;
 157         cipher = new NullCipher();
 158     }
 159 
 160     /**
 161      * Reads the next byte of data from this input stream. The value
 162      * byte is returned as an <code>int</code> in the range
 163      * <code>0</code> to <code>255</code>. If no byte is available
 164      * because the end of the stream has been reached, the value
 165      * <code>-1</code> is returned. This method blocks until input data
 166      * is available, the end of the stream is detected, or an exception
 167      * is thrown.
 168      * <p>
 169      *
 170      * @return  the next byte of data, or <code>-1</code> if the end of the
 171      *          stream is reached.
 172      * @exception  IOException  if an I/O error occurs.
 173      */
 174     public int read() throws IOException {
 175         if (ostart >= ofinish) {
 176             // we loop for new data as the spec says we are blocking
 177             int i = 0;
 178             while (i == 0) i = getMoreData();
 179             if (i == -1) return -1;
 180         }
 181         return ((int) obuffer[ostart++] & 0xff);
 182     };
 183 
 184     /**
 185      * Reads up to <code>b.length</code> bytes of data from this input
 186      * stream into an array of bytes.
 187      * <p>
 188      * The <code>read</code> method of <code>InputStream</code> calls
 189      * the <code>read</code> method of three arguments with the arguments
 190      * <code>b</code>, <code>0</code>, and <code>b.length</code>.
 191      *
 192      * @param      b   the buffer into which the data is read.
 193      * @return     the total number of bytes read into the buffer, or
 194      *             <code>-1</code> is there is no more data because the end of
 195      *             the stream has been reached.
 196      * @exception  IOException  if an I/O error occurs.
 197      * @see        java.io.InputStream#read(byte[], int, int)
 198      */
 199     public int read(byte b[]) throws IOException {
 200         return read(b, 0, b.length);
 201     }
 202 
 203     /**
 204      * Reads up to <code>len</code> bytes of data from this input stream
 205      * into an array of bytes. This method blocks until some input is
 206      * available. If the first argument is <code>null,</code> up to
 207      * <code>len</code> bytes are read and discarded.
 208      *
 209      * @param      b     the buffer into which the data is read.
 210      * @param      off   the start offset in the destination array
 211      *                   <code>buf</code>
 212      * @param      len   the maximum number of bytes read.
 213      * @return     the total number of bytes read into the buffer, or
 214      *             <code>-1</code> if there is no more data because the end of
 215      *             the stream has been reached.
 216      * @exception  IOException  if an I/O error occurs.
 217      * @see        java.io.InputStream#read()
 218      */
 219     public int read(byte b[], int off, int len) throws IOException {
 220         if (ostart >= ofinish) {
 221             // we loop for new data as the spec says we are blocking
 222             int i = 0;
 223             while (i == 0) i = getMoreData();
 224             if (i == -1) return -1;
 225         }
 226         if (len <= 0) {
 227             return 0;
 228         }
 229         int available = ofinish - ostart;
 230         if (len < available) available = len;
 231         if (b != null) {
 232             System.arraycopy(obuffer, ostart, b, off, available);
 233         }
 234         ostart = ostart + available;
 235         return available;
 236     }
 237 
 238     /**
 239      * Skips <code>n</code> bytes of input from the bytes that can be read
 240      * from this input stream without blocking.
 241      *
 242      * <p>Fewer bytes than requested might be skipped.
 243      * The actual number of bytes skipped is equal to <code>n</code> or
 244      * the result of a call to
 245      * {@link #available() available},
 246      * whichever is smaller.
 247      * If <code>n</code> is less than zero, no bytes are skipped.
 248      *
 249      * <p>The actual number of bytes skipped is returned.
 250      *
 251      * @param      n the number of bytes to be skipped.
 252      * @return     the actual number of bytes skipped.
 253      * @exception  IOException  if an I/O error occurs.
 254      */
 255     public long skip(long n) throws IOException {
 256         int available = ofinish - ostart;
 257         if (n > available) {
 258             n = available;
 259         }
 260         if (n < 0) {
 261             return 0;
 262         }
 263         ostart += n;
 264         return n;
 265     }
 266 
 267     /**
 268      * Returns the number of bytes that can be read from this input
 269      * stream without blocking. The <code>available</code> method of
 270      * <code>InputStream</code> returns <code>0</code>. This method
 271      * <B>should</B> be overridden by subclasses.
 272      *
 273      * @return     the number of bytes that can be read from this input stream
 274      *             without blocking.
 275      * @exception  IOException  if an I/O error occurs.
 276      */
 277     public int available() throws IOException {
 278         return (ofinish - ostart);
 279     }
 280 
 281     /**
 282      * Closes this input stream and releases any system resources
 283      * associated with the stream.
 284      * <p>
 285      * The <code>close</code> method of <code>CipherInputStream</code>
 286      * calls the <code>close</code> method of its underlying input
 287      * stream.
 288      *
 289      * @exception  IOException  if an I/O error occurs.
 290      */
 291     public void close() throws IOException {
 292         if (closed) {
 293             return;
 294         }
 295 
 296         closed = true;
 297         input.close();
 298         try {
 299             // throw away the unprocessed data
 300             if (!done) {
 301                 cipher.doFinal();
 302             }
 303         }
 304         catch (BadPaddingException | IllegalBlockSizeException ex) {
 305         }
 306         ostart = 0;
 307         ofinish = 0;
 308     }
 309 
 310     /**
 311      * Tests if this input stream supports the <code>mark</code>
 312      * and <code>reset</code> methods, which it does not.
 313      *
 314      * @return  <code>false</code>, since this class does not support the
 315      *          <code>mark</code> and <code>reset</code> methods.
 316      * @see     java.io.InputStream#mark(int)
 317      * @see     java.io.InputStream#reset()
 318      */
 319     public boolean markSupported() {
 320         return false;
 321     }
 322 }