/* | 
|
 * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. | 
|
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|
 * | 
|
 * This code is free software; you can redistribute it and/or modify it | 
|
 * under the terms of the GNU General Public License version 2 only, as | 
|
 * published by the Free Software Foundation.  Oracle designates this | 
|
 * particular file as subject to the "Classpath" exception as provided | 
|
 * by Oracle in the LICENSE file that accompanied this code. | 
|
 * | 
|
 * This code is distributed in the hope that it will be useful, but WITHOUT | 
|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|
 * version 2 for more details (a copy is included in the LICENSE file that | 
|
 * accompanied this code). | 
|
 * | 
|
 * You should have received a copy of the GNU General Public License version | 
|
 * 2 along with this work; if not, write to the Free Software Foundation, | 
|
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|
 * | 
|
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|
 * or visit www.oracle.com if you need additional information or have any | 
|
 * questions. | 
|
*/  | 
|
package javax.crypto;  | 
|
import java.io.InputStream;  | 
|
import java.io.FilterInputStream;  | 
|
import java.io.IOException;  | 
|
import javax.crypto.BadPaddingException;  | 
|
import javax.crypto.IllegalBlockSizeException;  | 
|
/**  | 
|
* A CipherInputStream is composed of an InputStream and a Cipher so  | 
|
* that read() methods return data that are read in from the  | 
|
* underlying InputStream but have been additionally processed by the  | 
|
* Cipher. The Cipher must be fully initialized before being used by  | 
|
* a CipherInputStream.  | 
|
*  | 
|
* <p> For example, if the Cipher is initialized for decryption, the  | 
|
* CipherInputStream will attempt to read in data and decrypt them,  | 
|
* before returning the decrypted data.  | 
|
*  | 
|
* <p> This class adheres strictly to the semantics, especially the  | 
|
* failure semantics, of its ancestor classes  | 
|
* java.io.FilterInputStream and java.io.InputStream. This class has  | 
|
* exactly those methods specified in its ancestor classes, and  | 
|
* overrides them all. Moreover, this class catches all exceptions  | 
|
* that are not thrown by its ancestor classes. In particular, the  | 
|
* <code>skip</code> method skips, and the <code>available</code>  | 
|
* method counts only data that have been processed by the encapsulated Cipher.  | 
|
* This class may catch BadPaddingException and other exceptions thrown by  | 
|
* failed integrity checks during decryption. These exceptions are not  | 
|
* re-thrown, so the client may not be informed that integrity checks  | 
|
* failed. Because of this behavior, this class may not be suitable  | 
|
* for use with decryption in an authenticated mode of operation (e.g. GCM).  | 
|
* Applications that require authenticated encryption can use the Cipher API  | 
|
* directly as an alternative to using this class.  | 
|
*  | 
|
* <p> It is crucial for a programmer using this class not to use  | 
|
* methods that are not defined or overriden in this class (such as a  | 
|
* new method or constructor that is later added to one of the super  | 
|
* classes), because the design and implementation of those methods  | 
|
* are unlikely to have considered security impact with regard to  | 
|
* CipherInputStream.  | 
|
*  | 
|
* @author Li Gong  | 
|
* @see java.io.InputStream  | 
|
* @see java.io.FilterInputStream  | 
|
* @see javax.crypto.Cipher  | 
|
* @see javax.crypto.CipherOutputStream  | 
|
*  | 
|
* @since 1.4  | 
|
*/  | 
|
public class CipherInputStream extends FilterInputStream {  | 
|
    // the cipher engine to use to process stream data | 
|
private Cipher cipher;  | 
|
    // the underlying input stream | 
|
private InputStream input;  | 
|
    /* the buffer holding data that have been read in from the | 
|
       underlying stream, but have not been processed by the cipher | 
|
engine. the size 512 bytes is somewhat randomly chosen */  | 
|
private byte[] ibuffer = new byte[512];  | 
|
    // having reached the end of the underlying input stream | 
|
private boolean done = false;  | 
|
    /* the buffer holding data that have been processed by the cipher | 
|
engine, but have not been read out */  | 
|
private byte[] obuffer;  | 
|
    // the offset pointing to the next "new" byte | 
|
private int ostart = 0;  | 
|
    // the offset pointing to the last "new" byte | 
|
private int ofinish = 0;  | 
|
    // stream status | 
|
private boolean closed = false;  | 
|
    /** | 
|
     * private convenience function. | 
|
     * | 
|
     * Entry condition: ostart = ofinish | 
|
     * | 
|
     * Exit condition: ostart <= ofinish | 
|
     * | 
|
     * return (ofinish-ostart) (we have this many bytes for you) | 
|
     * return 0 (no data now, but could have more later) | 
|
     * return -1 (absolutely no more data) | 
|
     * | 
|
     * Note:  Exceptions are only thrown after the stream is completely read. | 
|
     * For AEAD ciphers a read() of any length will internally cause the | 
|
     * whole stream to be read fully and verify the authentication tag before | 
|
     * returning decrypted data or exceptions. | 
|
*/  | 
|
private int getMoreData() throws IOException {  | 
|
if (done) return -1;  | 
|
int readin = input.read(ibuffer);  | 
|
if (readin == -1) {  | 
|
done = true;  | 
|
            try { | 
|
obuffer = cipher.doFinal();  | 
|
} catch (IllegalBlockSizeException | BadPaddingException e) {  | 
|
obuffer = null;  | 
|
throw new IOException(e);  | 
|
}  | 
|
if (obuffer == null)  | 
|
return -1;  | 
|
            else { | 
|
ostart = 0;  | 
|
ofinish = obuffer.length;  | 
|
return ofinish;  | 
|
}  | 
|
}  | 
|
        try { | 
|
obuffer = cipher.update(ibuffer, 0, readin);  | 
|
} catch (IllegalStateException e) {  | 
|
obuffer = null;  | 
|
throw e;  | 
|
}  | 
|
ostart = 0;  | 
|
if (obuffer == null)  | 
|
ofinish = 0;  | 
|
else ofinish = obuffer.length;  | 
|
return ofinish;  | 
|
}  | 
|
    /** | 
|
     * Constructs a CipherInputStream from an InputStream and a | 
|
     * Cipher. | 
|
     * <br>Note: if the specified input stream or cipher is | 
|
     * null, a NullPointerException may be thrown later when | 
|
     * they are used. | 
|
     * @param is the to-be-processed input stream | 
|
     * @param c an initialized Cipher object | 
|
*/  | 
|
public CipherInputStream(InputStream is, Cipher c) {  | 
|
super(is);  | 
|
input = is;  | 
|
cipher = c;  | 
|
}  | 
|
    /** | 
|
     * Constructs a CipherInputStream from an InputStream without | 
|
     * specifying a Cipher. This has the effect of constructing a | 
|
     * CipherInputStream using a NullCipher. | 
|
     * <br>Note: if the specified input stream is null, a | 
|
     * NullPointerException may be thrown later when it is used. | 
|
     * @param is the to-be-processed input stream | 
|
*/  | 
|
protected CipherInputStream(InputStream is) {  | 
|
super(is);  | 
|
input = is;  | 
|
cipher = new NullCipher();  | 
|
}  | 
|
    /** | 
|
     * Reads the next byte of data from this input stream. The value | 
|
     * byte is returned as an <code>int</code> in the range | 
|
     * <code>0</code> to <code>255</code>. If no byte is available | 
|
     * because the end of the stream has been reached, the value | 
|
     * <code>-1</code> is returned. This method blocks until input data | 
|
     * is available, the end of the stream is detected, or an exception | 
|
     * is thrown. | 
|
     * <p> | 
|
     * | 
|
     * @return  the next byte of data, or <code>-1</code> if the end of the | 
|
     *          stream is reached. | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
     * @since JCE1.2 | 
|
*/  | 
|
public int read() throws IOException {  | 
|
if (ostart >= ofinish) {  | 
|
            // we loop for new data as the spec says we are blocking | 
|
int i = 0;  | 
|
while (i == 0) i = getMoreData();  | 
|
if (i == -1) return -1;  | 
|
}  | 
|
return ((int) obuffer[ostart++] & 0xff);  | 
|
};  | 
|
    /** | 
|
     * Reads up to <code>b.length</code> bytes of data from this input | 
|
     * stream into an array of bytes. | 
|
     * <p> | 
|
     * The <code>read</code> method of <code>InputStream</code> calls | 
|
     * the <code>read</code> method of three arguments with the arguments | 
|
     * <code>b</code>, <code>0</code>, and <code>b.length</code>. | 
|
     * | 
|
     * @param      b   the buffer into which the data is read. | 
|
     * @return     the total number of bytes read into the buffer, or | 
|
     *             <code>-1</code> is there is no more data because the end of | 
|
     *             the stream has been reached. | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
     * @see        java.io.InputStream#read(byte[], int, int) | 
|
     * @since      JCE1.2 | 
|
*/  | 
|
public int read(byte b[]) throws IOException {  | 
|
return read(b, 0, b.length);  | 
|
}  | 
|
    /** | 
|
     * Reads up to <code>len</code> bytes of data from this input stream | 
|
     * into an array of bytes. This method blocks until some input is | 
|
     * available. If the first argument is <code>null,</code> up to | 
|
     * <code>len</code> bytes are read and discarded. | 
|
     * | 
|
     * @param      b     the buffer into which the data is read. | 
|
     * @param      off   the start offset in the destination array | 
|
     *                   <code>buf</code> | 
|
     * @param      len   the maximum number of bytes read. | 
|
     * @return     the total number of bytes read into the buffer, or | 
|
     *             <code>-1</code> if there is no more data because the end of | 
|
     *             the stream has been reached. | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
     * @see        java.io.InputStream#read() | 
|
     * @since      JCE1.2 | 
|
*/  | 
|
public int read(byte b[], int off, int len) throws IOException {  | 
|
if (ostart >= ofinish) {  | 
|
            // we loop for new data as the spec says we are blocking | 
|
int i = 0;  | 
|
while (i == 0) i = getMoreData();  | 
|
if (i == -1) return -1;  | 
|
}  | 
|
if (len <= 0) {  | 
|
return 0;  | 
|
}  | 
|
int available = ofinish - ostart;  | 
|
if (len < available) available = len;  | 
|
if (b != null) {  | 
|
System.arraycopy(obuffer, ostart, b, off, available);  | 
|
}  | 
|
ostart = ostart + available;  | 
|
return available;  | 
|
}  | 
|
    /** | 
|
     * Skips <code>n</code> bytes of input from the bytes that can be read | 
|
     * from this input stream without blocking. | 
|
     * | 
|
     * <p>Fewer bytes than requested might be skipped. | 
|
     * The actual number of bytes skipped is equal to <code>n</code> or | 
|
     * the result of a call to | 
|
     * {@link #available() available}, | 
|
     * whichever is smaller. | 
|
     * If <code>n</code> is less than zero, no bytes are skipped. | 
|
     * | 
|
     * <p>The actual number of bytes skipped is returned. | 
|
     * | 
|
     * @param      n the number of bytes to be skipped. | 
|
     * @return     the actual number of bytes skipped. | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
     * @since JCE1.2 | 
|
*/  | 
|
public long skip(long n) throws IOException {  | 
|
int available = ofinish - ostart;  | 
|
if (n > available) {  | 
|
n = available;  | 
|
}  | 
|
if (n < 0) {  | 
|
return 0;  | 
|
}  | 
|
ostart += n;  | 
|
return n;  | 
|
}  | 
|
    /** | 
|
     * Returns the number of bytes that can be read from this input | 
|
     * stream without blocking. The <code>available</code> method of | 
|
     * <code>InputStream</code> returns <code>0</code>. This method | 
|
     * <B>should</B> be overridden by subclasses. | 
|
     * | 
|
     * @return     the number of bytes that can be read from this input stream | 
|
     *             without blocking. | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
     * @since      JCE1.2 | 
|
*/  | 
|
public int available() throws IOException {  | 
|
return (ofinish - ostart);  | 
|
}  | 
|
    /** | 
|
     * Closes this input stream and releases any system resources | 
|
     * associated with the stream. | 
|
     * <p> | 
|
     * The <code>close</code> method of <code>CipherInputStream</code> | 
|
     * calls the <code>close</code> method of its underlying input | 
|
     * stream. | 
|
     * | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
     * @since JCE1.2 | 
|
*/  | 
|
public void close() throws IOException {  | 
|
if (closed) {  | 
|
return;  | 
|
}  | 
|
closed = true;  | 
|
input.close();  | 
|
// Throw away the unprocessed data and throw no crypto exceptions.  | 
|
// AEAD ciphers are fully readed before closing. Any authentication  | 
|
        // exceptions would occur while reading. | 
|
if (!done) {  | 
|
            try { | 
|
cipher.doFinal();  | 
|
}  | 
|
catch (BadPaddingException | IllegalBlockSizeException ex) {  | 
|
// Catch exceptions as the rest of the stream is unused.  | 
|
}  | 
|
}  | 
|
ostart = 0;  | 
|
ofinish = 0;  | 
|
}  | 
|
    /** | 
|
     * Tests if this input stream supports the <code>mark</code> | 
|
     * and <code>reset</code> methods, which it does not. | 
|
     * | 
|
     * @return  <code>false</code>, since this class does not support the | 
|
     *          <code>mark</code> and <code>reset</code> methods. | 
|
     * @see     java.io.InputStream#mark(int) | 
|
     * @see     java.io.InputStream#reset() | 
|
     * @since   JCE1.2 | 
|
*/  | 
|
    public boolean markSupported() { | 
|
return false;  | 
|
}  | 
|
}  |