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 javax.crypto.NullCipher;
24import java.security.GeneralSecurityException;
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 final Cipher cipher;
39    private final int I_BUFFER_SIZE = 20;
40    private final byte[] i_buffer = new byte[I_BUFFER_SIZE];
41    private int index; // index of the bytes to return from o_buffer
42    private byte[] o_buffer;
43    private boolean finished;
44
45    /**
46     * Creates a new {@code CipherInputStream} instance for an {@code
47     * InputStream} and a cipher.
48     *
49     * @param is
50     *            the input stream to read data from.
51     * @param c
52     *            the cipher to process the data with.
53     */
54    public CipherInputStream(InputStream is, Cipher c) {
55        super(is);
56        this.cipher = c;
57    }
58
59    /**
60     * Creates a new {@code CipherInputStream} instance for an {@code
61     * InputStream} without a cipher.
62     * <p>
63     * A {@code NullCipher} is created and used to process the data.
64     *
65     * @param is
66     *            the input stream to read data from.
67     */
68    protected CipherInputStream(InputStream is) {
69        this(is, new NullCipher());
70    }
71
72    /**
73     * Reads the next byte from this cipher input stream.
74     *
75     * @return the next byte, or {@code -1} if the end of the stream is reached.
76     * @throws IOException
77     *             if an error occurs.
78     */
79    @Override
80    public int read() throws IOException {
81        if (finished) {
82            return ((o_buffer == null) || (index == o_buffer.length))
83                            ? -1
84                            : o_buffer[index++] & 0xFF;
85        }
86        if ((o_buffer != null) && (index < o_buffer.length)) {
87            return o_buffer[index++] & 0xFF;
88        }
89        index = 0;
90        o_buffer = null;
91        int num_read;
92        while (o_buffer == null) {
93            if ((num_read = in.read(i_buffer)) == -1) {
94                try {
95                    o_buffer = cipher.doFinal();
96                } catch (Exception e) {
97                    throw new IOException(e.getMessage());
98                }
99                finished = true;
100                break;
101            }
102            o_buffer = cipher.update(i_buffer, 0, num_read);
103        }
104        return read();
105    }
106
107    /**
108     * Reads the next {@code b.length} bytes from this input stream into buffer
109     * {@code b}.
110     *
111     * @param b
112     *            the buffer to be filled with data.
113     * @return the number of bytes filled into buffer {@code b}, or {@code -1}
114     *         if the end of the stream is reached.
115     * @throws IOException
116     *             if an error occurs.
117     */
118    @Override
119    public int read(byte[] b) throws IOException {
120        return read(b, 0, b.length);
121    }
122
123    /**
124     * Reads the next {@code len} bytes from this input stream into buffer
125     * {@code b} starting at offset {@code off}.
126     * <p>
127     * if {@code b} is {@code null}, the next {@code len} bytes are read and
128     * discarded.
129     *
130     * @param b
131     *            the buffer to be filled with data.
132     * @param off
133     *            the offset to start in the buffer.
134     * @param len
135     *            the maximum number of bytes to read.
136     * @return the number of bytes filled into buffer {@code b}, or {@code -1}
137     *         of the of the stream is reached.
138     * @throws IOException
139     *             if an error occurs.
140     * @throws NullPointerException
141     *             if the underlying input stream is {@code null}.
142     */
143    @Override
144    public int read(byte[] b, int off, int len) throws IOException {
145        if (in == null) {
146            throw new NullPointerException("Underlying input stream is null");
147        }
148
149        int read_b;
150        int i;
151        for (i=0; i<len; i++) {
152            if ((read_b = read()) == -1) {
153                return (i == 0) ? -1 : i;
154            }
155            if (b != null) {
156                b[off+i] = (byte) read_b;
157            }
158        }
159        return i;
160    }
161
162    /**
163     * Skips up to n bytes from this input stream.
164     * <p>
165     * The number of bytes skipped depends on the result of a call to
166     * {@link CipherInputStream#available() available}. The smaller of n and the
167     * result are the number of bytes being skipped.
168     *
169     * @param n
170     *            the number of bytes that should be skipped.
171     * @return the number of bytes actually skipped.
172     * @throws IOException
173     *             if an error occurs
174     */
175    @Override
176    public long skip(long n) throws IOException {
177        long i = 0;
178        int available = available();
179        if (available < n) {
180            n = available;
181        }
182        while ((i < n) && (read() != -1)) {
183            i++;
184        }
185        return i;
186    }
187
188    @Override
189    public int available() throws IOException {
190        return 0;
191    }
192
193    /**
194     * Closes this {@code CipherInputStream}, also closes the underlying input
195     * stream and call {@code doFinal} on the cipher object.
196     *
197     * @throws IOException
198     *             if an error occurs.
199     */
200    @Override
201    public void close() throws IOException {
202        in.close();
203        try {
204            cipher.doFinal();
205        } catch (GeneralSecurityException ignore) {
206            //do like RI does
207        }
208
209    }
210
211    /**
212     * Returns whether this input stream supports {@code mark} and
213     * {@code reset}, which it does not.
214     *
215     * @return false, since this input stream does not support {@code mark} and
216     *         {@code reset}.
217     */
218    @Override
219    public boolean markSupported() {
220        return false;
221    }
222}
223