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 java.io.FilterInputStream; 42: import java.io.IOException; 43: import java.io.InputStream; 44: 45: /** 46: * This is an {@link java.io.InputStream} that filters its data 47: * through a {@link Cipher} before returning it. The <code>Cipher</code> 48: * argument must have been initialized before it is passed to the 49: * constructor. 50: * 51: * @author Casey Marshall (csm@gnu.org) 52: */ 53: public class CipherInputStream extends FilterInputStream 54: { 55: 56: // Constants and variables. 57: // ------------------------------------------------------------------------ 58: 59: /** 60: * The underlying {@link Cipher} instance. 61: */ 62: private Cipher cipher; 63: 64: /** 65: * Data that has been transformed but not read. 66: */ 67: private byte[] outBuffer; 68: 69: /** 70: * The offset into {@link #outBuffer} where valid data starts. 71: */ 72: private int outOffset; 73: 74: /** 75: * The number of valid bytes in the {@link #outBuffer}. 76: */ 77: private int outLength; 78: 79: /** 80: * Byte buffer that is filled with raw data from the underlying input 81: * stream. 82: */ 83: private byte[][] inBuffer; 84: 85: /** 86: * The amount of bytes in inBuffer[0] that may be input to the cipher. 87: */ 88: private int inLength; 89: 90: /** 91: * We set this when the cipher block size is 1, meaning that we can 92: * transform any amount of data. 93: */ 94: private boolean isStream; 95: 96: private static final int VIRGIN = 0; // I am born. 97: private static final int LIVING = 1; // I am nailed to the hull. 98: private static final int DYING = 2; // I am eaten by sharks. 99: private static final int DEAD = 3; 100: private int state; 101: 102: // Constructors. 103: // ------------------------------------------------------------------------ 104: 105: /** 106: * Creates a new input stream with a source input stream and cipher. 107: * 108: * @param in The underlying input stream. 109: * @param cipher The cipher to filter data through. 110: */ 111: public CipherInputStream(InputStream in, Cipher cipher) 112: { 113: this(in); 114: this.cipher = cipher; 115: if (!(isStream = cipher.getBlockSize() == 1)) 116: { 117: inBuffer = new byte[2][]; 118: inBuffer[0] = new byte[cipher.getBlockSize()]; 119: inBuffer[1] = new byte[cipher.getBlockSize()]; 120: inLength = 0; 121: outBuffer = new byte[cipher.getBlockSize()]; 122: outOffset = outLength = 0; 123: state = VIRGIN; 124: } 125: } 126: 127: /** 128: * Creates a new input stream without a cipher. This constructor is 129: * <code>protected</code> because this class does not work without an 130: * underlying cipher. 131: * 132: * @param in The underlying input stream. 133: */ 134: protected CipherInputStream(InputStream in) 135: { 136: super(in); 137: } 138: 139: // Instance methods overriding java.io.FilterInputStream. 140: // ------------------------------------------------------------------------ 141: 142: /** 143: * Returns the number of bytes available without blocking. The value 144: * returned by this method is never greater than the underlying 145: * cipher's block size. 146: * 147: * @return The number of bytes immediately available. 148: * @throws java.io.IOException If an I/O exception occurs. 149: */ 150: public int available() throws IOException 151: { 152: if (isStream) 153: return super.available(); 154: return outLength - outOffset; 155: } 156: 157: /** 158: * Close this input stream. This method merely calls the {@link 159: * java.io.InputStream#close()} method of the underlying input stream. 160: * 161: * @throws java.io.IOException If an I/O exception occurs. 162: */ 163: public void close() throws IOException 164: { 165: super.close(); 166: } 167: 168: /** 169: * Read a single byte from this input stream; returns -1 on the 170: * end-of-file. 171: * 172: * @return The byte read, or -1 if there are no more bytes. 173: * @throws java.io.IOExcpetion If an I/O exception occurs. 174: */ 175: public int read() throws IOException 176: { 177: if (isStream) 178: { 179: byte[] buf = new byte[1]; 180: int in = super.read(); 181: if (in == -1) 182: return -1; 183: buf[0] = (byte) in; 184: try 185: { 186: cipher.update(buf, 0, 1, buf, 0); 187: } 188: catch (ShortBufferException shouldNotHappen) 189: { 190: throw new IOException(shouldNotHappen.getMessage()); 191: } 192: return buf[0] & 0xFF; 193: } 194: if (state == DEAD) return -1; 195: if (available() == 0) nextBlock(); 196: if (state == DEAD) return -1; 197: return outBuffer[outOffset++] & 0xFF; 198: } 199: 200: /** 201: * Read bytes into an array, returning the number of bytes read or -1 202: * on the end-of-file. 203: * 204: * @param buf The byte array to read into. 205: * @param off The offset in <code>buf</code> to start. 206: * @param len The maximum number of bytes to read. 207: * @return The number of bytes read, or -1 on the end-of-file. 208: * @throws java.io.IOException If an I/O exception occurs. 209: */ 210: public int read(byte[] buf, int off, int len) throws IOException 211: { 212: if (isStream) 213: { 214: len = super.read(buf, off, len); 215: try 216: { 217: cipher.update(buf, off, len, buf, off); 218: } 219: catch (ShortBufferException shouldNotHappen) 220: { 221: throw new IOException(shouldNotHappen.getMessage()); 222: } 223: return len; 224: } 225: 226: int count = 0; 227: while (count < len) 228: { 229: if (available() == 0) 230: nextBlock(); 231: if (state == DEAD) 232: { 233: if (count > 0) return count; 234: else return -1; 235: } 236: int l = Math.min(available(), len - count); 237: System.arraycopy(outBuffer, outOffset, buf, count+off, l); 238: count += l; 239: outOffset = outLength = 0; 240: } 241: return count; 242: } 243: 244: /** 245: * Read bytes into an array, returning the number of bytes read or -1 246: * on the end-of-file. 247: * 248: * @param buf The byte arry to read into. 249: * @return The number of bytes read, or -1 on the end-of-file. 250: * @throws java.io.IOException If an I/O exception occurs. 251: */ 252: public int read(byte[] buf) throws IOException 253: { 254: return read(buf, 0, buf.length); 255: } 256: 257: /** 258: * Skip a number of bytes. This class only supports skipping as many 259: * bytes as are returned by {@link #available()}, which is the number 260: * of transformed bytes currently in this class's internal buffer. 261: * 262: * @param bytes The number of bytes to skip. 263: * @return The number of bytes skipped. 264: */ 265: public long skip(long bytes) throws IOException 266: { 267: if (isStream) 268: { 269: return super.skip(bytes); 270: } 271: long ret = 0; 272: if (bytes > 0 && available() > 0) 273: { 274: ret = available(); 275: outOffset = outLength = 0; 276: } 277: return ret; 278: } 279: 280: /** 281: * Returns whether or not this input stream supports the {@link 282: * #mark(long)} and {@link #reset()} methods; this input stream does 283: * not, however, and invariably returns <code>false</code>. 284: * 285: * @return <code>false</code> 286: */ 287: public boolean markSupported() 288: { 289: return false; 290: } 291: 292: /** 293: * Set the mark. This method is unsupported and is empty. 294: * 295: * @param mark Is ignored. 296: */ 297: public void mark(int mark) 298: { 299: } 300: 301: /** 302: * Reset to the mark. This method is unsupported and is empty. 303: */ 304: public void reset() throws IOException 305: { 306: throw new IOException("reset not supported"); 307: } 308: 309: // Own methods. 310: // ------------------------------------------------------------------------- 311: 312: private void nextBlock() throws IOException 313: { 314: byte[] temp = inBuffer[0]; 315: inBuffer[0] = inBuffer[1]; 316: inBuffer[1] = temp; 317: int count = 0; 318: boolean eof = false; 319: 320: if (state == VIRGIN || state == LIVING) 321: { 322: do 323: { 324: int l = in.read(inBuffer[1], count, inBuffer[1].length - count); 325: if (l == -1) 326: { 327: eof = true; 328: break; 329: } 330: count += l; 331: } 332: while (count < inBuffer[1].length); 333: } 334: 335: try 336: { 337: switch (state) 338: { 339: case VIRGIN: 340: state = LIVING; 341: nextBlock(); 342: break; 343: case LIVING: 344: if (eof) 345: { 346: if (count > 0) 347: { 348: outOffset = cipher.update(inBuffer[0], 0, inLength, outBuffer, 0); 349: state = DYING; 350: } 351: else 352: { 353: outOffset = cipher.doFinal(inBuffer[0], 0, inLength, outBuffer, 0); 354: state = DEAD; 355: } 356: } 357: else 358: { 359: outOffset = cipher.update(inBuffer[0], 0, inLength, outBuffer, 0); 360: } 361: break; 362: case DYING: 363: outOffset = cipher.doFinal(inBuffer[0], 0, inLength, outBuffer, 0); 364: state = DEAD; 365: break; 366: case DEAD: 367: } 368: } 369: catch (ShortBufferException sbe) 370: { 371: throw new IOException(sbe.toString()); 372: } 373: catch (BadPaddingException bpe) 374: { 375: throw new IOException(bpe.toString()); 376: } 377: catch (IllegalBlockSizeException ibse) 378: { 379: throw new IOException(ibse.toString()); 380: } 381: inLength = count; 382: } 383: }