PageRenderTime 26ms CodeModel.GetById 15ms app.highlight 7ms RepoModel.GetById 2ms app.codeStats 0ms

/extern/spongycastle/prov/src/main/java/org/spongycastle/jcajce/io/CipherInputStream.java

https://gitlab.com/vizilo/fdroidclient
Java | 217 lines | 137 code | 22 blank | 58 comment | 17 complexity | 5365dc1855944b0d29d0e05181953509 MD5 | raw file
  1package org.spongycastle.jcajce.io;
  2
  3import java.io.FilterInputStream;
  4import java.io.IOException;
  5import java.io.InputStream;
  6import java.security.GeneralSecurityException;
  7
  8import javax.crypto.BadPaddingException;
  9import javax.crypto.Cipher;
 10import javax.crypto.IllegalBlockSizeException;
 11
 12import org.spongycastle.crypto.io.InvalidCipherTextIOException;
 13
 14/**
 15 * A CipherInputStream is composed of an InputStream and a cipher so that read() methods return data
 16 * that are read in from the underlying InputStream but have been additionally processed by the
 17 * Cipher. The cipher must be fully initialized before being used by a CipherInputStream.
 18 * <p/>
 19 * For example, if the Cipher is initialized for decryption, the CipherInputStream will attempt to
 20 * read in data and decrypt them, before returning the decrypted data.
 21 * <p/>
 22 * This is a reimplementation of {@link javax.crypto.CipherInputStream} that is safe for use with
 23 * AEAD block ciphers, and does not silently catch {@link BadPaddingException} and
 24 * {@link IllegalBlockSizeException} errors. Any errors that occur during {@link Cipher#doFinal()
 25 * finalisation} are rethrown wrapped in an {@link InvalidCipherTextIOException}.
 26 */
 27public class CipherInputStream
 28    extends FilterInputStream
 29{
 30    private final Cipher cipher;
 31    private final byte[] inputBuffer = new byte[512];
 32    private boolean finalized = false;
 33    private byte[] buf;
 34    private int maxBuf;
 35    private int bufOff;
 36
 37    /**
 38     * Constructs a CipherInputStream from an InputStream and an initialised Cipher.
 39     */
 40    public CipherInputStream(InputStream input, Cipher cipher)
 41    {
 42        super(input);
 43        this.cipher = cipher;
 44    }
 45
 46    /**
 47     * Read data from underlying stream and process with cipher until end of stream or some data is
 48     * available after cipher processing.
 49     *
 50     * @return -1 to indicate end of stream, or the number of bytes (> 0) available.
 51     */
 52    private int nextChunk()
 53        throws IOException
 54    {
 55        if (finalized)
 56        {
 57            return -1;
 58        }
 59
 60        bufOff = 0;
 61        maxBuf = 0;
 62
 63        // Keep reading until EOF or cipher processing produces data
 64        while (maxBuf == 0)
 65        {
 66            int read = in.read(inputBuffer);
 67            if (read == -1)
 68            {
 69                buf = finaliseCipher();
 70                if ((buf == null) || (buf.length == 0))
 71                {
 72                    return -1;
 73                }
 74                maxBuf = buf.length;
 75                return maxBuf;
 76            }
 77
 78            buf = cipher.update(inputBuffer, 0, read);
 79            if (buf != null)
 80            {
 81                maxBuf = buf.length;
 82            }
 83        }
 84        return maxBuf;
 85    }
 86
 87    private byte[] finaliseCipher()
 88        throws InvalidCipherTextIOException
 89    {
 90        try
 91        {
 92            finalized = true;
 93            return cipher.doFinal();
 94        }
 95        catch (GeneralSecurityException e)
 96        {
 97            throw new InvalidCipherTextIOException("Error finalising cipher", e);
 98        }
 99    }
100
101    /**
102     * Reads data from the underlying stream and processes it with the cipher until the cipher
103     * outputs data, and returns the next available byte.
104     * <p/>
105     * If the underlying stream is exhausted by this call, the cipher will be finalised.
106     *
107     * @throws IOException if there was an error closing the input stream.
108     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
109     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
110     */
111    public int read()
112        throws IOException
113    {
114        if (bufOff >= maxBuf)
115        {
116            if (nextChunk() < 0)
117            {
118                return -1;
119            }
120        }
121
122        return buf[bufOff++] & 0xff;
123    }
124
125    /**
126     * Reads data from the underlying stream and processes it with the cipher until the cipher
127     * outputs data, and then returns up to <code>len</code> bytes in the provided array.
128     * <p/>
129     * If the underlying stream is exhausted by this call, the cipher will be finalised.
130     *
131     * @param b   the buffer into which the data is read.
132     * @param off the start offset in the destination array <code>b</code>
133     * @param len the maximum number of bytes read.
134     * @return the total number of bytes read into the buffer, or <code>-1</code> if there is no
135     *         more data because the end of the stream has been reached.
136     * @throws IOException if there was an error closing the input stream.
137     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
138     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
139     */
140    public int read(byte[] b, int off, int len)
141        throws IOException
142    {
143        if (bufOff >= maxBuf)
144        {
145            if (nextChunk() < 0)
146            {
147                return -1;
148            }
149        }
150
151        int toSupply = Math.min(len, available());
152        System.arraycopy(buf, bufOff, b, off, toSupply);
153        bufOff += toSupply;
154        return toSupply;
155    }
156
157    public long skip(long n)
158        throws IOException
159    {
160        if (n <= 0)
161        {
162            return 0;
163        }
164
165        int skip = (int)Math.min(n, available());
166        bufOff += skip;
167        return skip;
168    }
169
170    public int available()
171        throws IOException
172    {
173        return maxBuf - bufOff;
174    }
175
176    /**
177     * Closes the underlying input stream, and then finalises the processing of the data by the
178     * cipher.
179     *
180     * @throws IOException if there was an error closing the input stream.
181     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
182     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
183     */
184    public void close()
185        throws IOException
186    {
187        try
188        {
189            in.close();
190        }
191        finally
192        {
193            if (!finalized)
194            {
195                // Reset the cipher, discarding any data buffered in it
196                // Errors in cipher finalisation trump I/O error closing input
197                finaliseCipher();
198            }
199        }
200        maxBuf = bufOff = 0;
201    }
202
203    public void mark(int readlimit)
204    {
205    }
206
207    public void reset()
208        throws IOException
209    {
210    }
211
212    public boolean markSupported()
213    {
214        return false;
215    }
216
217}