/* * (non-Javadoc) * * @see java.io.FileInputStream#skip(long) */ @Override public long skip(long n) throws IOException { long bytesSkipped = 0; int toSkip; int bytesRead; byte[] skipBuf = new byte[config.getBlockSize()]; if (n < 0) { throw new IOException("Negative skip count"); } while (bytesSkipped < n) { toSkip = (int) Math.min(n - bytesSkipped, config.getBlockSize()); bytesRead = this.read(skipBuf, 0, toSkip); bytesSkipped += bytesRead; if (bytesRead == -1) { return -1; // Already at EOF } else if (bytesRead < toSkip) { return bytesSkipped; // Hit EOF now } } return bytesSkipped; }
/** * Create a new EncFSInputStream for reading decrypted data off a file on an EncFS volume * * @param volume Volume hosting the file to read * @param in Input stream to access the raw (encrypted) file contents * @param volumePath Volume path of the file being decrypted (needed for externalIVChaining) * @throws EncFSCorruptDataException File data is corrupt * @throws EncFSUnsupportedException Unsupported EncFS configuration * @throws IOException File provider returned I/O error */ public EncFSInputStream(EncFSVolume volume, InputStream in, String volumePath) throws EncFSCorruptDataException, EncFSUnsupportedException { super(in); this.volume = volume; this.config = volume.getConfig(); this.blockSize = config.getBlockSize(); this.numMACBytes = config.getBlockMACBytes(); this.numRandBytes = config.getBlockMACRandBytes(); this.blockHeaderSize = this.numMACBytes + this.numRandBytes; this.blockBuf = null; this.bufCursor = 0; this.blockNum = 0; if (config.isUniqueIV()) { // Compute file IV byte[] fileHeader = new byte[EncFSFile.HEADER_SIZE]; try { in.read(fileHeader); } catch (IOException e) { throw new EncFSCorruptDataException("Could't read file IV"); } byte[] initIv; if (config.isExternalIVChaining()) { /* * When using external IV chaining we compute initIv based on * the file path. */ initIv = EncFSCrypto.computeChainIv(volume, volumePath); } else { // When not using external IV chaining initIv is just zero's. initIv = new byte[8]; } try { this.fileIv = EncFSCrypto.streamDecode(volume, initIv, fileHeader); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { throw new EncFSCorruptDataException(e); } catch (BadPaddingException e) { throw new EncFSCorruptDataException(e); } } else { // No unique IV per file, just use 0 this.fileIv = new byte[EncFSFile.HEADER_SIZE]; } }
/* * Read one block (blockSize bytes) of data from the underlying * FileInputStream, decrypt it and store it in blockBuf for consumption via * read() methods */ private int readBlock() throws IOException, EncFSCorruptDataException, EncFSUnsupportedException { byte[] cipherBuf = new byte[blockSize]; boolean zeroBlock = false; int bytesRead = 0; int lastBytesRead = 0; // Read until we read a whole block or we reach the end of the input while (bytesRead < blockSize) { lastBytesRead = in.read(cipherBuf, bytesRead, blockSize - bytesRead); if (lastBytesRead > 0) { bytesRead += lastBytesRead; } else if (lastBytesRead < 0) { /* * If we read some bytes return that, if not then we're at the * end of the stream */ if (bytesRead == 0) { bytesRead = -1; } break; } } if (bytesRead == blockSize) { // block decode /* * If file holes are allowed then we need to test whether the whole * block is made up of 0's. If not (which is going to be the case * for MAC header by default), we will do block decryption. */ if (config.isHolesAllowed()) { zeroBlock = true; for (int i = 0; i < cipherBuf.length; i++) if (cipherBuf[i] != 0) { zeroBlock = false; break; } } try { if (zeroBlock == true) { blockBuf = cipherBuf; } else { blockBuf = EncFSCrypto.blockDecode(volume, getBlockIV(), cipherBuf); } } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { throw new EncFSCorruptDataException(e); } catch (BadPaddingException e) { throw new EncFSCorruptDataException(e); } bufCursor = blockHeaderSize; blockNum++; } else if (bytesRead > 0) { // stream decode try { blockBuf = EncFSCrypto.streamDecode(volume, getBlockIV(), cipherBuf, 0, bytesRead); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (IllegalBlockSizeException e) { throw new EncFSCorruptDataException(e); } catch (BadPaddingException e) { throw new EncFSCorruptDataException(e); } bufCursor = blockHeaderSize; blockNum++; } // Verify the block header if ((bytesRead > 0) && (blockHeaderSize > 0) && (zeroBlock == false)) { byte mac[] = EncFSCrypto.mac64(volume.getMac(), blockBuf, numMACBytes); for (int i = 0; i < numMACBytes; i++) { if (mac[7 - i] != blockBuf[i]) { throw new EncFSCorruptDataException("Block MAC mismatch"); } } } return bytesRead; }