CipherInputStream.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. /* CipherInputStream.java -- Filters input through a cipher.
  2. Copyright (C) 2004 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package javax.crypto;
  32. import gnu.classpath.Configuration;
  33. import gnu.classpath.debug.Component;
  34. import gnu.classpath.debug.SystemLogger;
  35. import java.io.FilterInputStream;
  36. import java.io.IOException;
  37. import java.io.InputStream;
  38. import java.util.logging.Logger;
  39. /**
  40. * This is an {@link java.io.InputStream} that filters its data
  41. * through a {@link Cipher} before returning it. The <code>Cipher</code>
  42. * argument must have been initialized before it is passed to the
  43. * constructor.
  44. *
  45. * @author Casey Marshall (csm@gnu.org)
  46. */
  47. public class CipherInputStream extends FilterInputStream
  48. {
  49. // Constants and variables.
  50. // ------------------------------------------------------------------------
  51. private static final Logger logger = SystemLogger.SYSTEM;
  52. /**
  53. * The underlying {@link Cipher} instance.
  54. */
  55. private final Cipher cipher;
  56. /**
  57. * Data that has been transformed but not read.
  58. */
  59. private byte[] outBuffer;
  60. /**
  61. * The offset into {@link #outBuffer} where valid data starts.
  62. */
  63. private int outOffset;
  64. /**
  65. * We set this when the cipher block size is 1, meaning that we can
  66. * transform any amount of data.
  67. */
  68. private final boolean isStream;
  69. /**
  70. * Whether or not we've reached the end of the stream.
  71. */
  72. private boolean eof;
  73. // Constructors.
  74. // ------------------------------------------------------------------------
  75. /**
  76. * Creates a new input stream with a source input stream and cipher.
  77. *
  78. * @param in The underlying input stream.
  79. * @param cipher The cipher to filter data through.
  80. */
  81. public CipherInputStream(InputStream in, Cipher cipher)
  82. {
  83. super (in);
  84. this.cipher = cipher;
  85. isStream = cipher.getBlockSize () == 1;
  86. eof = false;
  87. if (Configuration.DEBUG)
  88. logger.log (Component.CRYPTO, "I am born; cipher: {0}, stream? {1}",
  89. new Object[] { cipher.getAlgorithm (),
  90. Boolean.valueOf (isStream) });
  91. }
  92. /**
  93. * Creates a new input stream without a cipher. This constructor is
  94. * <code>protected</code> because this class does not work without an
  95. * underlying cipher.
  96. *
  97. * @param in The underlying input stream.
  98. */
  99. protected CipherInputStream(InputStream in)
  100. {
  101. this (in, new NullCipher ());
  102. }
  103. // Instance methods overriding java.io.FilterInputStream.
  104. // ------------------------------------------------------------------------
  105. /**
  106. * Returns the number of bytes available without blocking. The value
  107. * returned is the number of bytes that have been processed by the
  108. * cipher, and which are currently buffered by this class.
  109. *
  110. * @return The number of bytes immediately available.
  111. * @throws java.io.IOException If an I/O exception occurs.
  112. */
  113. public int available() throws IOException
  114. {
  115. if (isStream)
  116. return super.available();
  117. if (outBuffer == null || outOffset >= outBuffer.length)
  118. nextBlock ();
  119. return outBuffer.length - outOffset;
  120. }
  121. /**
  122. * Close this input stream. This method merely calls the {@link
  123. * java.io.InputStream#close()} method of the underlying input stream.
  124. *
  125. * @throws java.io.IOException If an I/O exception occurs.
  126. */
  127. public synchronized void close() throws IOException
  128. {
  129. super.close();
  130. }
  131. /**
  132. * Read a single byte from this input stream; returns -1 on the
  133. * end-of-file.
  134. *
  135. * @return The byte read, or -1 if there are no more bytes.
  136. * @throws java.io.IOExcpetion If an I/O exception occurs.
  137. */
  138. public synchronized int read() throws IOException
  139. {
  140. if (isStream)
  141. {
  142. byte[] buf = new byte[1];
  143. int in = super.read();
  144. if (in == -1)
  145. return -1;
  146. buf[0] = (byte) in;
  147. try
  148. {
  149. cipher.update(buf, 0, 1, buf, 0);
  150. }
  151. catch (ShortBufferException shouldNotHappen)
  152. {
  153. throw new IOException(shouldNotHappen.getMessage());
  154. }
  155. return buf[0] & 0xFF;
  156. }
  157. if (outBuffer == null || outOffset == outBuffer.length)
  158. {
  159. if (eof)
  160. return -1;
  161. nextBlock ();
  162. }
  163. return outBuffer [outOffset++] & 0xFF;
  164. }
  165. /**
  166. * Read bytes into an array, returning the number of bytes read or -1
  167. * on the end-of-file.
  168. *
  169. * @param buf The byte array to read into.
  170. * @param off The offset in <code>buf</code> to start.
  171. * @param len The maximum number of bytes to read.
  172. * @return The number of bytes read, or -1 on the end-of-file.
  173. * @throws java.io.IOException If an I/O exception occurs.
  174. */
  175. public synchronized int read(byte[] buf, int off, int len)
  176. throws IOException
  177. {
  178. // CipherInputStream has this wierd implementation where if
  179. // the buffer is null, this call is the same as `skip'.
  180. if (buf == null)
  181. return (int) skip (len);
  182. if (isStream)
  183. {
  184. len = super.read(buf, off, len);
  185. if (len > 0)
  186. {
  187. try
  188. {
  189. cipher.update(buf, off, len, buf, off);
  190. }
  191. catch (ShortBufferException shouldNotHappen)
  192. {
  193. IOException ioe = new IOException ("Short buffer for stream cipher -- this should not happen");
  194. ioe.initCause (shouldNotHappen);
  195. throw ioe;
  196. }
  197. }
  198. return len;
  199. }
  200. int count = 0;
  201. while (count < len)
  202. {
  203. if (outBuffer == null || outOffset >= outBuffer.length)
  204. {
  205. if (eof)
  206. {
  207. if (count == 0)
  208. count = -1;
  209. break;
  210. }
  211. nextBlock();
  212. }
  213. int l = Math.min (outBuffer.length - outOffset, len - count);
  214. System.arraycopy (outBuffer, outOffset, buf, count+off, l);
  215. count += l;
  216. outOffset += l;
  217. }
  218. return count;
  219. }
  220. /**
  221. * Read bytes into an array, returning the number of bytes read or -1
  222. * on the end-of-file.
  223. *
  224. * @param buf The byte arry to read into.
  225. * @return The number of bytes read, or -1 on the end-of-file.
  226. * @throws java.io.IOException If an I/O exception occurs.
  227. */
  228. public int read(byte[] buf) throws IOException
  229. {
  230. return read(buf, 0, buf.length);
  231. }
  232. /**
  233. * Skip a number of bytes. This class only supports skipping as many
  234. * bytes as are returned by {@link #available()}, which is the number
  235. * of transformed bytes currently in this class's internal buffer.
  236. *
  237. * @param bytes The number of bytes to skip.
  238. * @return The number of bytes skipped.
  239. */
  240. public long skip(long bytes) throws IOException
  241. {
  242. if (isStream)
  243. {
  244. return super.skip(bytes);
  245. }
  246. long ret = 0;
  247. if (bytes > 0 && outBuffer != null && outOffset >= outBuffer.length)
  248. {
  249. ret = outBuffer.length - outOffset;
  250. outOffset = outBuffer.length;
  251. }
  252. return ret;
  253. }
  254. /**
  255. * Returns whether or not this input stream supports the {@link
  256. * #mark(long)} and {@link #reset()} methods; this input stream does
  257. * not, however, and invariably returns <code>false</code>.
  258. *
  259. * @return <code>false</code>
  260. */
  261. public boolean markSupported()
  262. {
  263. return false;
  264. }
  265. /**
  266. * Set the mark. This method is unsupported and is empty.
  267. *
  268. * @param mark Is ignored.
  269. */
  270. public void mark(int mark)
  271. {
  272. }
  273. /**
  274. * Reset to the mark. This method is unsupported and is empty.
  275. */
  276. public void reset() throws IOException
  277. {
  278. throw new IOException("reset not supported");
  279. }
  280. // Own methods.
  281. // -------------------------------------------------------------------------
  282. // FIXME: I don't fully understand how this class is supposed to work.
  283. private void nextBlock() throws IOException
  284. {
  285. byte[] buf = new byte[cipher.getBlockSize ()];
  286. if (Configuration.DEBUG)
  287. logger.log (Component.CRYPTO, "getting a new data block");
  288. try
  289. {
  290. outBuffer = null;
  291. outOffset = 0;
  292. while (outBuffer == null)
  293. {
  294. int l = in.read (buf);
  295. if (Configuration.DEBUG)
  296. logger.log (Component.CRYPTO, "we read {0} bytes",
  297. Integer.valueOf (l));
  298. if (l == -1)
  299. {
  300. outBuffer = cipher.doFinal ();
  301. eof = true;
  302. return;
  303. }
  304. outOffset = 0;
  305. outBuffer = cipher.update (buf, 0, l);
  306. }
  307. }
  308. catch (BadPaddingException bpe)
  309. {
  310. IOException ioe = new IOException ("bad padding");
  311. ioe.initCause (bpe);
  312. throw ioe;
  313. }
  314. catch (IllegalBlockSizeException ibse)
  315. {
  316. IOException ioe = new IOException ("illegal block size");
  317. ioe.initCause (ibse);
  318. throw ioe;
  319. }
  320. finally
  321. {
  322. if (Configuration.DEBUG)
  323. logger.log (Component.CRYPTO,
  324. "decrypted {0} bytes for reading",
  325. Integer.valueOf (outBuffer.length));
  326. }
  327. }
  328. }