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