001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.commons.crypto.stream;
019
020import java.io.IOException;
021import java.io.InputStream;
022import java.lang.reflect.Method;
023import java.nio.ByteBuffer;
024import java.nio.channels.ReadableByteChannel;
025import java.security.GeneralSecurityException;
026import java.security.Key;
027import java.security.spec.AlgorithmParameterSpec;
028import java.util.Objects;
029import java.util.Properties;
030
031import javax.crypto.BadPaddingException;
032import javax.crypto.Cipher;
033import javax.crypto.IllegalBlockSizeException;
034import javax.crypto.ShortBufferException;
035import javax.crypto.spec.IvParameterSpec;
036
037import org.apache.commons.crypto.Crypto;
038import org.apache.commons.crypto.cipher.CryptoCipher;
039import org.apache.commons.crypto.stream.input.ChannelInput;
040import org.apache.commons.crypto.stream.input.Input;
041import org.apache.commons.crypto.stream.input.StreamInput;
042import org.apache.commons.crypto.utils.Utils;
043
044/**
045 * CryptoInputStream reads input data and decrypts data in stream manner. It
046 * supports any mode of operations such as AES CBC/CTR/GCM mode in concept.It is
047 * not thread-safe.
048 *
049 */
050
051public class CryptoInputStream extends InputStream implements
052        ReadableByteChannel {
053    private final byte[] oneByteBuf = new byte[1];
054
055    /**
056     * The configuration key of the buffer size for stream.
057     */
058    public static final String STREAM_BUFFER_SIZE_KEY = Crypto.CONF_PREFIX
059            + "stream.buffer.size";
060
061    /** The CryptoCipher instance. */
062    final CryptoCipher cipher; // package protected for access by crypto classes; do not expose further
063
064    /** The buffer size. */
065    private final int bufferSize;
066
067    /** Crypto key for the cipher. */
068    final Key key; // package protected for access by crypto classes; do not expose further
069
070    /** the algorithm parameters */
071    private final AlgorithmParameterSpec params;
072
073    /** Flag to mark whether the input stream is closed. */
074    private boolean closed;
075
076    /**
077     * Flag to mark whether do final of the cipher to end the decrypting stream.
078     */
079    private boolean finalDone = false;
080
081    /** The input data. */
082    Input input; // package protected for access by crypto classes; do not expose further
083
084    /**
085     * Input data buffer. The data starts at inBuffer.position() and ends at to
086     * inBuffer.limit().
087     */
088    ByteBuffer inBuffer; // package protected for access by crypto classes; do not expose further
089
090    /**
091     * The decrypted data buffer. The data starts at outBuffer.position() and
092     * ends at outBuffer.limit().
093     */
094    ByteBuffer outBuffer; // package protected for access by crypto classes; do not expose further
095
096    // stream related configuration keys
097    /**
098     * The default value of the buffer size for stream.
099     */
100    private static final int STREAM_BUFFER_SIZE_DEFAULT = 8192;
101
102    private static final int MIN_BUFFER_SIZE = 512;
103
104    /**
105     * Constructs a {@link CryptoInputStream}.
106     *
107     * @param transformation the name of the transformation, e.g.,
108     * <i>AES/CBC/PKCS5Padding</i>.
109     * See the Java Cryptography Architecture Standard Algorithm Name Documentation
110     * for information about standard transformation names.
111     * @param properties The {@code Properties} class represents a set of
112     *        properties.
113     * @param inputStream the input stream.
114     * @param key crypto key for the cipher.
115     * @param params the algorithm parameters.
116     * @throws IOException if an I/O error occurs.
117     */
118    @SuppressWarnings("resource") // The CryptoCipher returned by getCipherInstance() is closed by CryptoInputStream.
119    public CryptoInputStream(final String transformation,
120            final Properties properties, final InputStream inputStream, final Key key,
121            final AlgorithmParameterSpec params) throws IOException {
122        this(inputStream, Utils.getCipherInstance(transformation, properties),
123                CryptoInputStream.getBufferSize(properties), key, params);
124    }
125
126    /**
127     * Constructs a {@link CryptoInputStream}.
128     *
129     * @param transformation the name of the transformation, e.g.,
130     * <i>AES/CBC/PKCS5Padding</i>.
131     * See the Java Cryptography Architecture Standard Algorithm Name Documentation
132     * for information about standard transformation names.
133     * @param properties The {@code Properties} class represents a set of
134     *        properties.
135     * @param channel the ReadableByteChannel object.
136     * @param key crypto key for the cipher.
137     * @param params the algorithm parameters.
138     * @throws IOException if an I/O error occurs.
139     */
140    @SuppressWarnings("resource") // The CryptoCipher returned by getCipherInstance() is closed by CryptoInputStream.
141    public CryptoInputStream(final String transformation,
142            final Properties properties, final ReadableByteChannel channel, final Key key,
143            final AlgorithmParameterSpec params) throws IOException {
144        this(channel, Utils.getCipherInstance(transformation, properties), CryptoInputStream
145                .getBufferSize(properties), key, params);
146    }
147
148    /**
149     * Constructs a {@link CryptoInputStream}.
150     *
151     * @param cipher the cipher instance.
152     * @param inputStream the input stream.
153     * @param bufferSize the bufferSize.
154     * @param key crypto key for the cipher.
155     * @param params the algorithm parameters.
156     * @throws IOException if an I/O error occurs.
157     */
158    protected CryptoInputStream(final InputStream inputStream, final CryptoCipher cipher,
159            final int bufferSize, final Key key, final AlgorithmParameterSpec params)
160            throws IOException {
161        this(new StreamInput(inputStream, bufferSize), cipher, bufferSize, key, params);
162    }
163
164    /**
165     * Constructs a {@link CryptoInputStream}.
166     *
167     * @param channel the ReadableByteChannel instance.
168     * @param cipher the cipher instance.
169     * @param bufferSize the bufferSize.
170     * @param key crypto key for the cipher.
171     * @param params the algorithm parameters.
172     * @throws IOException if an I/O error occurs.
173     */
174    protected CryptoInputStream(final ReadableByteChannel channel, final CryptoCipher cipher,
175            final int bufferSize, final Key key, final AlgorithmParameterSpec params)
176            throws IOException {
177        this(new ChannelInput(channel), cipher, bufferSize, key, params);
178    }
179
180    /**
181     * Constructs a {@link CryptoInputStream}.
182     *
183     * @param input the input data.
184     * @param cipher the cipher instance.
185     * @param bufferSize the bufferSize.
186     * @param key crypto key for the cipher.
187     * @param params the algorithm parameters.
188     * @throws IOException if an I/O error occurs.
189     */
190    protected CryptoInputStream(final Input input, final CryptoCipher cipher, final int bufferSize,
191            final Key key, final AlgorithmParameterSpec params) throws IOException {
192        this.input = input;
193        this.cipher = cipher;
194        this.bufferSize = CryptoInputStream.checkBufferSize(cipher, bufferSize);
195
196        this.key = key;
197        this.params = params;
198        if (!(params instanceof IvParameterSpec)) {
199            // other AlgorithmParameterSpec such as GCMParameterSpec is not
200            // supported now.
201            throw new IOException("Illegal parameters");
202        }
203
204        inBuffer = ByteBuffer.allocateDirect(this.bufferSize);
205        outBuffer = ByteBuffer.allocateDirect(this.bufferSize
206                + cipher.getBlockSize());
207        outBuffer.limit(0);
208
209        initCipher();
210    }
211
212    /**
213     * Overrides the {@link java.io.InputStream#read()}. Reads the next byte of
214     * data from the input stream.
215     *
216     * @return the next byte of data, or {@code -1} if the end of the
217     *         stream is reached.
218     * @throws IOException if an I/O error occurs.
219     */
220    @Override
221    public int read() throws IOException {
222        int n;
223        while ((n = read(oneByteBuf, 0, 1)) == 0) { //NOPMD
224            /* no op */
225        }
226        return (n == -1) ? -1 : oneByteBuf[0] & 0xff;
227    }
228
229    /**
230     * Overrides the {@link java.io.InputStream#read(byte[], int, int)}.
231     * Decryption is buffer based. If there is data in {@link #outBuffer}, then
232     * read it out of this buffer. If there is no data in {@link #outBuffer},
233     * then read more from the underlying stream and do the decryption.
234     *
235     * @param array the buffer into which the decrypted data is read.
236     * @param off the buffer offset.
237     * @param len the maximum number of decrypted data bytes to read.
238     * @return int the total number of decrypted data bytes read into the
239     *         buffer.
240     * @throws IOException if an I/O error occurs.
241     */
242    @Override
243    public int read(final byte[] array, final int off, final int len) throws IOException {
244        checkStream();
245        Objects.requireNonNull(array, "array");
246        if (off < 0 || len < 0 || len > array.length - off) {
247            throw new IndexOutOfBoundsException();
248        } else if (len == 0) {
249            return 0;
250        }
251
252        final int remaining = outBuffer.remaining();
253        if (remaining > 0) {
254            // Satisfy the read with the existing data
255            final int n = Math.min(len, remaining);
256            outBuffer.get(array, off, n);
257            return n;
258        }
259        // No data in the out buffer, try read new data and decrypt it
260        // we loop for new data
261        int nd = 0;
262        while (nd == 0) {
263            nd = decryptMore();
264        }
265        if (nd < 0) {
266            return nd;
267        }
268
269        final int n = Math.min(len, outBuffer.remaining());
270        outBuffer.get(array, off, n);
271        return n;
272    }
273
274    /**
275     * Overrides the {@link java.io.InputStream#skip(long)}. Skips over and
276     * discards {@code n} bytes of data from this input stream.
277     *
278     * @param n the number of bytes to be skipped.
279     * @return the actual number of bytes skipped.
280     * @throws IOException if an I/O error occurs.
281     */
282    @Override
283    public long skip(final long n) throws IOException {
284        Utils.checkArgument(n >= 0, "Negative skip length.");
285        checkStream();
286
287        if (n == 0) {
288            return 0;
289        }
290
291        long remaining = n;
292        int nd;
293
294        while (remaining > 0) {
295            if (remaining <= outBuffer.remaining()) {
296                // Skip in the remaining buffer
297                final int pos = outBuffer.position() + (int) remaining;
298                outBuffer.position(pos);
299
300                remaining = 0;
301                break;
302            }
303            remaining -= outBuffer.remaining();
304            outBuffer.clear();
305
306            // we loop for new data
307            nd = 0;
308            while (nd == 0) {
309                nd = decryptMore();
310            }
311            if (nd < 0) {
312                break;
313            }
314        }
315
316        return n - remaining;
317    }
318
319    /**
320     * Overrides the {@link InputStream#available()}. Returns an estimate of the
321     * number of bytes that can be read (or skipped over) from this input stream
322     * without blocking by the next invocation of a method for this input
323     * stream.
324     *
325     * @return an estimate of the number of bytes that can be read (or skipped
326     *         over) from this input stream without blocking or {@code 0} when
327     *         it reaches the end of the input stream.
328     * @throws IOException if an I/O error occurs.
329     */
330    @Override
331    public int available() throws IOException {
332        checkStream();
333
334        return input.available() + outBuffer.remaining();
335    }
336
337    /**
338     * Overrides the {@link InputStream#close()}. Closes this input stream and
339     * releases any system resources associated with the stream.
340     *
341     * @throws IOException if an I/O error occurs.
342     */
343    @Override
344    public void close() throws IOException {
345        if (closed) {
346            return;
347        }
348
349        input.close();
350        freeBuffers();
351        cipher.close();
352        super.close();
353        closed = true;
354    }
355
356    /**
357     * Overrides the {@link InputStream#markSupported()}.
358     *
359     * @return false,the {@link CtrCryptoInputStream} don't support the mark
360     *         method.
361     */
362    @Override
363    public boolean markSupported() {
364        return false;
365    }
366
367    /**
368     * Overrides the {@link java.nio.channels.Channel#isOpen()}.
369     *
370     * @return {@code true} if, and only if, this channel is open.
371     */
372    @Override
373    public boolean isOpen() {
374        return !closed;
375    }
376
377    /**
378     * Overrides the
379     * {@link java.nio.channels.ReadableByteChannel#read(ByteBuffer)}. Reads a
380     * sequence of bytes from this channel into the given buffer.
381     *
382     * @param dst The buffer into which bytes are to be transferred.
383     * @return The number of bytes read, possibly zero, or {@code -1} if the
384     *         channel has reached end-of-stream.
385     * @throws IOException if an I/O error occurs.
386     */
387    @Override
388    public int read(final ByteBuffer dst) throws IOException {
389        checkStream();
390        int remaining = outBuffer.remaining();
391        if (remaining <= 0) {
392            // Decrypt more data
393            // we loop for new data
394            int nd = 0;
395            while (nd == 0) {
396                nd = decryptMore();
397            }
398
399            if (nd < 0) {
400                return -1;
401            }
402        }
403
404        // Copy decrypted data from outBuffer to dst
405        remaining = outBuffer.remaining();
406        final int toRead = dst.remaining();
407        if (toRead <= remaining) {
408            final int limit = outBuffer.limit();
409            outBuffer.limit(outBuffer.position() + toRead);
410            dst.put(outBuffer);
411            outBuffer.limit(limit);
412            return toRead;
413        }
414        dst.put(outBuffer);
415        return remaining;
416    }
417
418    /**
419     * Gets the buffer size.
420     *
421     * @return the bufferSize.
422     */
423    protected int getBufferSize() {
424        return bufferSize;
425    }
426
427    /**
428     * Gets the key.
429     *
430     * @return the key.
431     */
432    protected Key getKey() {
433        return key;
434    }
435
436    /**
437     * Gets the internal CryptoCipher.
438     *
439     * @return the cipher instance.
440     */
441    protected CryptoCipher getCipher() {
442        return cipher;
443    }
444
445    /**
446     * Gets the specification of cryptographic parameters.
447     *
448     * @return the params.
449     */
450    protected AlgorithmParameterSpec getParams() {
451        return params;
452    }
453
454    /**
455     * Gets the input.
456     *
457     * @return the input.
458     */
459    protected Input getInput() {
460        return input;
461    }
462
463    /**
464     * Initializes the cipher.
465     *
466     * @throws IOException if an I/O error occurs.
467     */
468    protected void initCipher() throws IOException {
469        try {
470            cipher.init(Cipher.DECRYPT_MODE, key, params);
471        } catch (final GeneralSecurityException e) {
472            throw new IOException(e);
473        }
474    }
475
476    /**
477     * Decrypts more data by reading the under layer stream. The decrypted data
478     * will be put in the output buffer. If the end of the under stream reached,
479     * we will do final of the cipher to finish all the decrypting of data.
480     *
481     * @return The number of decrypted data.
482     *           return -1 (if end of the decrypted stream)
483     *           return 0 (no data now, but could have more later)
484     * @throws IOException if an I/O error occurs.
485     */
486    protected int decryptMore() throws IOException {
487        if (finalDone) {
488            return -1;
489        }
490
491        final int n = input.read(inBuffer);
492        if (n < 0) {
493            // The stream is end, finalize the cipher stream
494            decryptFinal();
495
496            // Satisfy the read with the remaining
497            final int remaining = outBuffer.remaining();
498            if (remaining > 0) {
499                return remaining;
500            }
501
502            // End of the stream
503            return -1;
504        } else if (n == 0) {
505            // No data is read, but the stream is not end yet
506            return 0;
507        } else {
508            decrypt();
509            return outBuffer.remaining();
510        }
511    }
512
513    /**
514     * Does the decryption using inBuffer as input and outBuffer as output. Upon
515     * return, inBuffer is cleared; the decrypted data starts at
516     * outBuffer.position() and ends at outBuffer.limit().
517     *
518     * @throws IOException if an I/O error occurs.
519     */
520    protected void decrypt() throws IOException {
521        // Prepare the input buffer and clear the out buffer
522        inBuffer.flip();
523        outBuffer.clear();
524
525        try {
526            cipher.update(inBuffer, outBuffer);
527        } catch (final ShortBufferException e) {
528            throw new IOException(e);
529        }
530
531        // Clear the input buffer and prepare out buffer
532        inBuffer.clear();
533        outBuffer.flip();
534    }
535
536    /**
537     * Does final of the cipher to end the decrypting stream.
538     *
539     * @throws IOException if an I/O error occurs.
540     */
541    protected void decryptFinal() throws IOException {
542        // Prepare the input buffer and clear the out buffer
543        inBuffer.flip();
544        outBuffer.clear();
545
546        try {
547            cipher.doFinal(inBuffer, outBuffer);
548            finalDone = true;
549        } catch (final ShortBufferException e) {
550            throw new IOException(e);
551        } catch (final IllegalBlockSizeException e) {
552            throw new IOException(e);
553        } catch (final BadPaddingException e) {
554            throw new IOException(e);
555        }
556
557        // Clear the input buffer and prepare out buffer
558        inBuffer.clear();
559        outBuffer.flip();
560    }
561
562    /**
563     * Checks whether the stream is closed.
564     *
565     * @throws IOException if an I/O error occurs.
566     */
567    protected void checkStream() throws IOException {
568        if (closed) {
569            throw new IOException("Stream closed");
570        }
571    }
572
573    /** Forcibly free the direct buffers. */
574    protected void freeBuffers() {
575        CryptoInputStream.freeDirectBuffer(inBuffer);
576        CryptoInputStream.freeDirectBuffer(outBuffer);
577    }
578
579    /**
580     * Forcibly free the direct buffer.
581     *
582     * @param buffer the bytebuffer to be freed.
583     */
584    static void freeDirectBuffer(final ByteBuffer buffer) {
585        try {
586            /* Using reflection to implement sun.nio.ch.DirectBuffer.cleaner()
587            .clean(); */
588            final String SUN_CLASS = "sun.nio.ch.DirectBuffer";
589            final Class<?>[] interfaces = buffer.getClass().getInterfaces();
590
591            for (final Class<?> clazz : interfaces) {
592                if (clazz.getName().equals(SUN_CLASS)) {
593                    final Object[] NO_PARAM = new Object[0];
594                    /* DirectBuffer#cleaner() */
595                    final Method getCleaner = Class.forName(SUN_CLASS).getMethod("cleaner");
596                    final Object cleaner = getCleaner.invoke(buffer, NO_PARAM);
597                    /* Cleaner#clean() */
598                    final Method cleanMethod = Class.forName("sun.misc.Cleaner").getMethod("clean");
599                    cleanMethod.invoke(cleaner, NO_PARAM);
600                    return;
601                }
602            }
603        } catch (final ReflectiveOperationException e) { // NOPMD
604            // Ignore the Reflection exception.
605        }
606    }
607
608    /**
609     * Reads crypto buffer size.
610     *
611     * @param props The {@code Properties} class represents a set of
612     *        properties.
613     * @return the buffer size.
614     * */
615    static int getBufferSize(final Properties props) {
616        final String bufferSizeStr = props.getProperty(CryptoInputStream.STREAM_BUFFER_SIZE_KEY);
617        if (bufferSizeStr == null || bufferSizeStr.isEmpty()) {
618            return CryptoInputStream.STREAM_BUFFER_SIZE_DEFAULT;
619        }
620        return Integer.parseInt(bufferSizeStr);
621    }
622
623    /**
624     * Checks whether the cipher is supported streaming.
625     *
626     * @param cipher the {@link CryptoCipher} instance.
627     * @throws IOException if an I/O error occurs.
628     */
629    static void checkStreamCipher(final CryptoCipher cipher)
630            throws IOException {
631        if (!cipher.getAlgorithm().equals("AES/CTR/NoPadding")) {
632            throw new IOException("AES/CTR/NoPadding is required");
633        }
634    }
635
636    /**
637     * Checks and floors buffer size.
638     *
639     * @param cipher the {@link CryptoCipher} instance.
640     * @param bufferSize the buffer size.
641     * @return the remaining buffer size.
642     */
643    static int checkBufferSize(final CryptoCipher cipher, final int bufferSize) {
644        Utils.checkArgument(bufferSize >= CryptoInputStream.MIN_BUFFER_SIZE,
645                "Minimum value of buffer size is " + CryptoInputStream.MIN_BUFFER_SIZE + ".");
646        return bufferSize - bufferSize
647                % cipher.getBlockSize();
648    }
649}