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