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
18package javax.crypto;
19
20import java.io.FilterInputStream;
21import java.io.IOException;
22import java.io.InputStream;
23import java.security.GeneralSecurityException;
24import 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 */
36public 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("Error while finalizing cipher", e);
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