Home | History | Annotate | Download | only in crypto
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package javax.crypto;
     19 
     20 import java.io.FilterInputStream;
     21 import java.io.IOException;
     22 import java.io.InputStream;
     23 import java.security.GeneralSecurityException;
     24 import libcore.io.Streams;
     25 
     26 /**
     27  * This class wraps an {@code InputStream} and a cipher so that {@code read()}
     28  * methods return data that are read from the underlying {@code InputStream} and
     29  * processed by the cipher.
     30  * <p>
     31  * The cipher must be initialized for the requested operation before being used
     32  * by a {@code CipherInputStream}. For example, if a cipher initialized for
     33  * decryption is used with a {@code CipherInputStream}, the {@code
     34  * CipherInputStream} tries to read the data an decrypt them before returning.
     35  */
     36 public class CipherInputStream extends FilterInputStream {
     37 
     38     private static final int I_BUFFER_SIZE = 20;
     39 
     40     private final Cipher cipher;
     41     private final byte[] inputBuffer = new byte[I_BUFFER_SIZE];
     42     private byte[] outputBuffer;
     43     private int outputIndex; // index of the first byte to return from outputBuffer
     44     private int outputLength; // count of the bytes to return from outputBuffer
     45     private boolean finished;
     46 
     47     /**
     48      * Creates a new {@code CipherInputStream} instance for an {@code
     49      * InputStream} and a cipher.
     50      *
     51      * <p><strong>Warning:</strong> passing a null source creates an invalid
     52      * {@code CipherInputStream}. All read operations on such a stream will
     53      * fail.
     54      *
     55      * @param is
     56      *            the input stream to read data from.
     57      * @param c
     58      *            the cipher to process the data with.
     59      */
     60     public CipherInputStream(InputStream is, Cipher c) {
     61         super(is);
     62         this.cipher = c;
     63     }
     64 
     65     /**
     66      * Creates a new {@code CipherInputStream} instance for an {@code
     67      * InputStream} without a cipher.
     68      * <p>
     69      * A {@code NullCipher} is created and used to process the data.
     70      *
     71      * @param is
     72      *            the input stream to read data from.
     73      */
     74     protected CipherInputStream(InputStream is) {
     75         this(is, new NullCipher());
     76     }
     77 
     78     /**
     79      * Reads the next byte from this cipher input stream.
     80      *
     81      * @return the next byte, or {@code -1} if the end of the stream is reached.
     82      * @throws IOException
     83      *             if an error occurs.
     84      */
     85     @Override
     86     public int read() throws IOException {
     87         if (finished) {
     88             return (outputIndex == outputLength) ? -1 : outputBuffer[outputIndex++] & 0xFF;
     89         }
     90         if (outputIndex < outputLength) {
     91             return outputBuffer[outputIndex++] & 0xFF;
     92         }
     93         outputIndex = 0;
     94         outputLength = 0;
     95         while (outputLength == 0) {
     96             // check output size on each iteration since pending state
     97             // in the cipher can cause this to vary from call to call
     98             int outputSize = cipher.getOutputSize(inputBuffer.length);
     99             if ((outputBuffer == null) || (outputBuffer.length < outputSize)) {
    100                 this.outputBuffer = new byte[outputSize];
    101             }
    102             int byteCount = in.read(inputBuffer);
    103             if (byteCount == -1) {
    104                 try {
    105                     outputLength = cipher.doFinal(outputBuffer, 0);
    106                 } catch (Exception e) {
    107                     throw new IOException(e.getMessage());
    108                 }
    109                 finished = true;
    110                 break;
    111             }
    112             try {
    113                 outputLength = cipher.update(inputBuffer, 0, byteCount, outputBuffer, 0);
    114             } catch (ShortBufferException e) {
    115                 throw new AssertionError(e);  // should not happen since we sized with getOutputSize
    116             }
    117         }
    118         return read();
    119     }
    120 
    121     /**
    122      * Reads the next {@code len} bytes from this input stream into buffer
    123      * {@code buf} starting at offset {@code off}.
    124      * <p>
    125      * if {@code buf} is {@code null}, the next {@code len} bytes are read and
    126      * discarded.
    127      *
    128      * @return the number of bytes filled into buffer {@code buf}, or {@code -1}
    129      *         of the of the stream is reached.
    130      * @throws IOException
    131      *             if an error occurs.
    132      * @throws NullPointerException
    133      *             if the underlying input stream is {@code null}.
    134      */
    135     @Override
    136     public int read(byte[] buf, int off, int len) throws IOException {
    137         if (in == null) {
    138             throw new NullPointerException("in == null");
    139         }
    140 
    141         int i;
    142         for (i = 0; i < len; ++i) {
    143             int b = read();
    144             if (b == -1) {
    145                 return (i == 0) ? -1 : i;
    146             }
    147             if (buf != null) {
    148                 buf[off+i] = (byte) b;
    149             }
    150         }
    151         return i;
    152     }
    153 
    154     @Override
    155     public long skip(long byteCount) throws IOException {
    156         return Streams.skipByReading(this, byteCount);
    157     }
    158 
    159     @Override
    160     public int available() throws IOException {
    161         return 0;
    162     }
    163 
    164     /**
    165      * Closes this {@code CipherInputStream}, also closes the underlying input
    166      * stream and call {@code doFinal} on the cipher object.
    167      *
    168      * @throws IOException
    169      *             if an error occurs.
    170      */
    171     @Override
    172     public void close() throws IOException {
    173         in.close();
    174         try {
    175             cipher.doFinal();
    176         } catch (GeneralSecurityException ignore) {
    177             //do like RI does
    178         }
    179 
    180     }
    181 
    182     /**
    183      * Returns whether this input stream supports {@code mark} and
    184      * {@code reset}, which it does not.
    185      *
    186      * @return false, since this input stream does not support {@code mark} and
    187      *         {@code reset}.
    188      */
    189     @Override
    190     public boolean markSupported() {
    191         return false;
    192     }
    193 }
    194