/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

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