1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package javax.crypto; 19 20 import java.io.FilterInputStream; 21 import java.io.IOException; 22 import java.io.InputStream; 23 import java.security.GeneralSecurityException; 24 import libcore.io.Streams; 25 26 /** 27 * This class wraps an {@code InputStream} and a cipher so that {@code read()} 28 * methods return data that are read from the underlying {@code InputStream} and 29 * processed by the cipher. 30 * <p> 31 * The cipher must be initialized for the requested operation before being used 32 * by a {@code CipherInputStream}. For example, if a cipher initialized for 33 * decryption is used with a {@code CipherInputStream}, the {@code 34 * CipherInputStream} tries to read the data an decrypt them before returning. 35 */ 36 public class CipherInputStream extends FilterInputStream { 37 38 private static final int I_BUFFER_SIZE = 20; 39 40 private final Cipher cipher; 41 private final byte[] inputBuffer = new byte[I_BUFFER_SIZE]; 42 private byte[] outputBuffer; 43 private int outputIndex; // index of the first byte to return from outputBuffer 44 private int outputLength; // count of the bytes to return from outputBuffer 45 private boolean finished; 46 47 /** 48 * Creates a new {@code CipherInputStream} instance for an {@code 49 * InputStream} and a cipher. 50 * 51 * <p><strong>Warning:</strong> passing a null source creates an invalid 52 * {@code CipherInputStream}. All read operations on such a stream will 53 * fail. 54 * 55 * @param is 56 * the input stream to read data from. 57 * @param c 58 * the cipher to process the data with. 59 */ 60 public CipherInputStream(InputStream is, Cipher c) { 61 super(is); 62 this.cipher = c; 63 } 64 65 /** 66 * Creates a new {@code CipherInputStream} instance for an {@code 67 * InputStream} without a cipher. 68 * <p> 69 * A {@code NullCipher} is created and used to process the data. 70 * 71 * @param is 72 * the input stream to read data from. 73 */ 74 protected CipherInputStream(InputStream is) { 75 this(is, new NullCipher()); 76 } 77 78 /** 79 * Reads the next byte from this cipher input stream. 80 * 81 * @return the next byte, or {@code -1} if the end of the stream is reached. 82 * @throws IOException 83 * if an error occurs. 84 */ 85 @Override 86 public int read() throws IOException { 87 if (finished) { 88 return (outputIndex == outputLength) ? -1 : outputBuffer[outputIndex++] & 0xFF; 89 } 90 if (outputIndex < outputLength) { 91 return outputBuffer[outputIndex++] & 0xFF; 92 } 93 outputIndex = 0; 94 outputLength = 0; 95 while (outputLength == 0) { 96 // check output size on each iteration since pending state 97 // in the cipher can cause this to vary from call to call 98 int outputSize = cipher.getOutputSize(inputBuffer.length); 99 if ((outputBuffer == null) || (outputBuffer.length < outputSize)) { 100 this.outputBuffer = new byte[outputSize]; 101 } 102 int byteCount = in.read(inputBuffer); 103 if (byteCount == -1) { 104 try { 105 outputLength = cipher.doFinal(outputBuffer, 0); 106 } catch (Exception e) { 107 throw new IOException(e.getMessage()); 108 } 109 finished = true; 110 break; 111 } 112 try { 113 outputLength = cipher.update(inputBuffer, 0, byteCount, outputBuffer, 0); 114 } catch (ShortBufferException e) { 115 throw new AssertionError(e); // should not happen since we sized with getOutputSize 116 } 117 } 118 return read(); 119 } 120 121 /** 122 * Reads the next {@code len} bytes from this input stream into buffer 123 * {@code buf} starting at offset {@code off}. 124 * <p> 125 * if {@code buf} is {@code null}, the next {@code len} bytes are read and 126 * discarded. 127 * 128 * @return the number of bytes filled into buffer {@code buf}, or {@code -1} 129 * of the of the stream is reached. 130 * @throws IOException 131 * if an error occurs. 132 * @throws NullPointerException 133 * if the underlying input stream is {@code null}. 134 */ 135 @Override 136 public int read(byte[] buf, int off, int len) throws IOException { 137 if (in == null) { 138 throw new NullPointerException("Underlying input stream is null"); 139 } 140 141 int i; 142 for (i = 0; i < len; ++i) { 143 int b = read(); 144 if (b == -1) { 145 return (i == 0) ? -1 : i; 146 } 147 if (buf != null) { 148 buf[off+i] = (byte) b; 149 } 150 } 151 return i; 152 } 153 154 @Override 155 public long skip(long byteCount) throws IOException { 156 return Streams.skipByReading(this, byteCount); 157 } 158 159 @Override 160 public int available() throws IOException { 161 return 0; 162 } 163 164 /** 165 * Closes this {@code CipherInputStream}, also closes the underlying input 166 * stream and call {@code doFinal} on the cipher object. 167 * 168 * @throws IOException 169 * if an error occurs. 170 */ 171 @Override 172 public void close() throws IOException { 173 in.close(); 174 try { 175 cipher.doFinal(); 176 } catch (GeneralSecurityException ignore) { 177 //do like RI does 178 } 179 180 } 181 182 /** 183 * Returns whether this input stream supports {@code mark} and 184 * {@code reset}, which it does not. 185 * 186 * @return false, since this input stream does not support {@code mark} and 187 * {@code reset}. 188 */ 189 @Override 190 public boolean markSupported() { 191 return false; 192 } 193 } 194