Multiple updates
1) Bug fix for EncFSInputStream missing off the last byte of the stream 2) Initial support for EncFSFileOutputStream 3) Additional tests 4) EncFSComparer enhanced to check file read/ re-write
This commit is contained in:
parent
4d1754de1b
commit
e7c9798c57
8 changed files with 589 additions and 102 deletions
src
main/java/org/mrpdaemon/sec/encfs
test/java/org/mrpdaemon/sec/encfs
|
@ -272,15 +272,28 @@ public class EncFSCrypto {
|
|||
|
||||
public static byte[] blockDecode(EncFSVolume volume, byte[] ivSeed, byte[] data)
|
||||
throws InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
|
||||
if (data.length != volume.getConfig().getBlockSize()) {
|
||||
throw new IllegalBlockSizeException();
|
||||
}
|
||||
// if (data.length != volume.getConfig().getBlockSize()) {
|
||||
// throw new IllegalBlockSizeException();
|
||||
// }
|
||||
Cipher cipher = volume.getBlockCipher();
|
||||
cipherInit(volume, Cipher.DECRYPT_MODE, cipher, ivSeed);
|
||||
byte[] result = cipher.doFinal(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] blockEncode(EncFSVolume volume, byte[] ivSeed, byte[] data) throws IllegalBlockSizeException,
|
||||
InvalidAlgorithmParameterException, BadPaddingException {
|
||||
// if (data.length != volume.getConfig().getBlockSize()) {
|
||||
// throw new
|
||||
// IllegalBlockSizeException("Data length must match block size ("
|
||||
// + volume.getConfig().getBlockSize() + " vs. " + data.length);
|
||||
// }
|
||||
Cipher cipher = volume.getBlockCipher();
|
||||
cipherInit(volume, Cipher.ENCRYPT_MODE, cipher, ivSeed);
|
||||
byte[] result = cipher.doFinal(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] streamEncode(EncFSVolume volume, byte[] ivSeed, byte[] data) throws EncFSUnsupportedException,
|
||||
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
|
||||
return streamEncode(volume.getStreamCipher(), volume.getMac(), volume.getKey(), volume.getIV(), ivSeed, data);
|
||||
|
@ -473,40 +486,24 @@ public class EncFSCrypto {
|
|||
fileIv[i] = (byte) (macBytes[i] ^ chainIv[i]);
|
||||
}
|
||||
|
||||
Cipher cipher;
|
||||
byte[] decFileName;
|
||||
|
||||
if (volume.getConfig().getNameAlgorithm() == EncFSConfig.ENCFS_CONFIG_NAME_ALG_BLOCK) {
|
||||
// Block decryption
|
||||
cipher = volume.getBlockCipher();
|
||||
|
||||
try {
|
||||
EncFSCrypto.cipherInit(volume, Cipher.DECRYPT_MODE, cipher, fileIv);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
decFileName = cipher.doFinal(encFileName);
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (BadPaddingException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
}
|
||||
|
||||
} else {
|
||||
// Stream decryption
|
||||
try {
|
||||
try {
|
||||
if (volume.getConfig().getNameAlgorithm() == EncFSConfig.ENCFS_CONFIG_NAME_ALG_BLOCK) {
|
||||
// Block decryption
|
||||
decFileName = EncFSCrypto.blockDecode(volume, fileIv, encFileName);
|
||||
} else {
|
||||
// Stream decryption
|
||||
decFileName = EncFSCrypto.streamDecode(volume, fileIv, encFileName);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (BadPaddingException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (EncFSUnsupportedException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
}
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (BadPaddingException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (EncFSUnsupportedException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
}
|
||||
|
||||
// Verify decryption worked
|
||||
|
@ -572,9 +569,23 @@ public class EncFSCrypto {
|
|||
*/
|
||||
public static String encodeName(EncFSVolume volume, String fileName, String volumePath)
|
||||
throws EncFSCorruptDataException {
|
||||
Cipher cipher;
|
||||
byte[] decFileName = fileName.getBytes();
|
||||
|
||||
byte[] paddedDecFileName;
|
||||
if (volume.getConfig().getNameAlgorithm() == EncFSConfig.ENCFS_CONFIG_NAME_ALG_BLOCK) {
|
||||
// Pad to the nearest 16 bytes, add a full block if needed
|
||||
int padBytesSize = 16;
|
||||
int padLen = padBytesSize - (decFileName.length % padBytesSize);
|
||||
if (padLen == 0) {
|
||||
padLen = padBytesSize;
|
||||
}
|
||||
paddedDecFileName = Arrays.copyOf(decFileName, decFileName.length + padLen);
|
||||
Arrays.fill(paddedDecFileName, decFileName.length, paddedDecFileName.length, (byte) padLen);
|
||||
} else {
|
||||
// Stream encryption
|
||||
paddedDecFileName = decFileName;
|
||||
}
|
||||
|
||||
byte[] chainIv = new byte[8];
|
||||
|
||||
// Chained IV computation
|
||||
|
@ -606,9 +617,9 @@ public class EncFSCrypto {
|
|||
|
||||
byte[] mac16;
|
||||
if (volume.getConfig().isChainedNameIV()) {
|
||||
mac16 = EncFSCrypto.mac16(volume.getMac(), decFileName, chainIv);
|
||||
mac16 = EncFSCrypto.mac16(volume.getMac(), paddedDecFileName, Arrays.copyOf(chainIv, chainIv.length));
|
||||
} else {
|
||||
mac16 = EncFSCrypto.mac16(volume.getMac(), decFileName);
|
||||
mac16 = EncFSCrypto.mac16(volume.getMac(), paddedDecFileName);
|
||||
}
|
||||
|
||||
// TODO: make sure its multiple of 16
|
||||
|
@ -622,38 +633,23 @@ public class EncFSCrypto {
|
|||
}
|
||||
|
||||
byte[] encFileName;
|
||||
if (volume.getConfig().getNameAlgorithm() == EncFSConfig.ENCFS_CONFIG_NAME_ALG_BLOCK) {
|
||||
try {
|
||||
if (volume.getConfig().getNameAlgorithm() == EncFSConfig.ENCFS_CONFIG_NAME_ALG_BLOCK) {
|
||||
// Block encryption
|
||||
encFileName = EncFSCrypto.blockEncode(volume, fileIv, paddedDecFileName);
|
||||
|
||||
// Block encryption
|
||||
cipher = volume.getBlockCipher();
|
||||
|
||||
try {
|
||||
EncFSCrypto.cipherInit(volume, Cipher.ENCRYPT_MODE, cipher, fileIv);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
encFileName = cipher.doFinal(decFileName);
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (BadPaddingException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
}
|
||||
|
||||
} else {
|
||||
// Stream encryption
|
||||
try {
|
||||
encFileName = EncFSCrypto.streamEncode(volume, fileIv, decFileName);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (BadPaddingException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (EncFSUnsupportedException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} else {
|
||||
// Stream encryption
|
||||
encFileName = EncFSCrypto.streamEncode(volume, fileIv, paddedDecFileName);
|
||||
}
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new EncFSCorruptDataException(e);
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new EncFSCorruptDataException(e);
|
||||
} catch (BadPaddingException e) {
|
||||
throw new EncFSCorruptDataException(e);
|
||||
} catch (EncFSUnsupportedException e) {
|
||||
throw new EncFSCorruptDataException(e);
|
||||
}
|
||||
|
||||
// current versions store the checksum at the beginning (encfs 0.x
|
||||
|
|
|
@ -342,4 +342,12 @@ public class EncFSFile {
|
|||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public String getAbsoluteName() {
|
||||
if (volumePath.endsWith("/")) {
|
||||
return volumePath + plaintextName;
|
||||
} else {
|
||||
return volumePath + "/" + plaintextName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.mrpdaemon.sec.encfs;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
@ -28,7 +29,7 @@ import javax.crypto.IllegalBlockSizeException;
|
|||
* FileInputStream extension that allows data to be read from a file on an EncFS
|
||||
* volume.
|
||||
*/
|
||||
public class EncFSFileInputStream extends FileInputStream {
|
||||
public class EncFSFileInputStream extends InputStream {
|
||||
|
||||
// Volume that underlying file belongs to
|
||||
private final EncFSVolume volume;
|
||||
|
@ -51,6 +52,8 @@ public class EncFSFileInputStream extends FileInputStream {
|
|||
// File IV computed from the first 8 bytes of the file
|
||||
private byte[] fileIv;
|
||||
|
||||
private final InputStream inStream;
|
||||
|
||||
/**
|
||||
* Create a new EncFSFileInputStream for reading data off a file on an EncFS
|
||||
* volume
|
||||
|
@ -67,8 +70,14 @@ public class EncFSFileInputStream extends FileInputStream {
|
|||
*/
|
||||
public EncFSFileInputStream(EncFSFile file) throws FileNotFoundException, EncFSCorruptDataException,
|
||||
EncFSUnsupportedException {
|
||||
super(file.getFile());
|
||||
this.volume = file.getVolume();
|
||||
this(file.getVolume(), new FileInputStream(file.getFile()));
|
||||
}
|
||||
|
||||
public EncFSFileInputStream(EncFSVolume volume, InputStream in) throws FileNotFoundException,
|
||||
EncFSCorruptDataException, EncFSUnsupportedException {
|
||||
super();
|
||||
this.inStream = in;
|
||||
this.volume = volume;
|
||||
this.config = volume.getConfig();
|
||||
this.blockSize = config.getBlockSize();
|
||||
this.blockBuf = null;
|
||||
|
@ -79,7 +88,7 @@ public class EncFSFileInputStream extends FileInputStream {
|
|||
// Compute file IV
|
||||
byte[] fileHeader = new byte[8];
|
||||
try {
|
||||
super.read(fileHeader);
|
||||
inStream.read(fileHeader);
|
||||
} catch (IOException e) {
|
||||
throw new EncFSCorruptDataException("Could't read file IV");
|
||||
}
|
||||
|
@ -115,7 +124,7 @@ public class EncFSFileInputStream extends FileInputStream {
|
|||
*/
|
||||
private int readBlock() throws IOException, EncFSCorruptDataException, EncFSUnsupportedException {
|
||||
byte[] cipherBuf = new byte[blockSize];
|
||||
int bytesRead = super.read(cipherBuf, 0, blockSize);
|
||||
int bytesRead = inStream.read(cipherBuf, 0, blockSize);
|
||||
if (bytesRead == blockSize) { // block decode
|
||||
try {
|
||||
blockBuf = EncFSCrypto.blockDecode(volume, getBlockIV(), cipherBuf);
|
||||
|
@ -187,7 +196,7 @@ public class EncFSFileInputStream extends FileInputStream {
|
|||
while (bytesRead < len) {
|
||||
|
||||
// Read more data if the data buffer is out
|
||||
if ((blockBuf == null) || (bufCursor == (blockBuf.length - 1))) {
|
||||
if ((blockBuf == null) || (bufCursor == (blockBuf.length))) {
|
||||
try {
|
||||
ret = readBlock();
|
||||
} catch (EncFSCorruptDataException e) {
|
||||
|
@ -205,7 +214,7 @@ public class EncFSFileInputStream extends FileInputStream {
|
|||
}
|
||||
}
|
||||
|
||||
bytesToCopy = Math.min(blockBuf.length - bufCursor - 1, len - bytesRead);
|
||||
bytesToCopy = Math.min(blockBuf.length - bufCursor, len - bytesRead);
|
||||
System.arraycopy(blockBuf, bufCursor, b, destOffset, bytesToCopy);
|
||||
|
||||
bufCursor += bytesToCopy;
|
||||
|
@ -257,4 +266,10 @@ public class EncFSFileInputStream extends FileInputStream {
|
|||
// TODO: could support mark()/reset()
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
inStream.close();
|
||||
super.close();
|
||||
}
|
||||
}
|
212
src/main/java/org/mrpdaemon/sec/encfs/EncFSFileOutputStream.java
Normal file
212
src/main/java/org/mrpdaemon/sec/encfs/EncFSFileOutputStream.java
Normal file
|
@ -0,0 +1,212 @@
|
|||
package org.mrpdaemon.sec.encfs;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilterOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
|
||||
public class EncFSFileOutputStream extends OutputStream {
|
||||
private final EncFSBufferedOutputStream encfsOs;
|
||||
|
||||
private static class EncFSBufferedOutputStream extends FilterOutputStream {
|
||||
private static final SecureRandom secureRandom = new SecureRandom();
|
||||
|
||||
private final EncFSVolume volume;
|
||||
private final EncFSConfig config;
|
||||
private final int blockSize;
|
||||
private byte[] fileIv;
|
||||
private byte[] fileHeader;
|
||||
|
||||
private final byte buf[];
|
||||
|
||||
private int count;
|
||||
private int blockNum;
|
||||
|
||||
private final Cipher blockCipher;
|
||||
|
||||
private final Cipher streamCipher;
|
||||
|
||||
public EncFSBufferedOutputStream(EncFSVolume volume, OutputStream out) throws EncFSUnsupportedException,
|
||||
EncFSCorruptDataException {
|
||||
super(out);
|
||||
this.volume = volume;
|
||||
this.config = volume.getConfig();
|
||||
this.blockSize = config.getBlockSize();
|
||||
|
||||
if (config.isUniqueIV()) {
|
||||
// Compute file IV
|
||||
this.fileHeader = new byte[8];
|
||||
|
||||
secureRandom.nextBytes(fileHeader);
|
||||
|
||||
byte[] zeroIv = new byte[8];
|
||||
// TODO: external IV chaining changes zeroIv
|
||||
try {
|
||||
this.fileIv = EncFSCrypto
|
||||
.streamDecode(volume, zeroIv, Arrays.copyOf(fileHeader, fileHeader.length));
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
} catch (BadPaddingException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
}
|
||||
} else {
|
||||
// No unique IV per file, just use 0
|
||||
this.fileIv = new byte[8];
|
||||
}
|
||||
|
||||
this.blockCipher = EncFSCrypto.newBlockCipher();
|
||||
try {
|
||||
EncFSCrypto.cipherInit(volume, Cipher.ENCRYPT_MODE, blockCipher, fileIv);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
}
|
||||
this.streamCipher = EncFSCrypto.newStreamCipher();
|
||||
try {
|
||||
EncFSCrypto.cipherInit(volume, Cipher.ENCRYPT_MODE, streamCipher, fileIv);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new EncFSCorruptDataException(e.getMessage());
|
||||
}
|
||||
|
||||
// written blocks are blockSize - 8 bytes for the MAC?
|
||||
buf = new byte[blockSize];
|
||||
}
|
||||
|
||||
/** Flush the internal buffer */
|
||||
private void writeBuffer(boolean isFinal) throws IOException {
|
||||
|
||||
if (isFinal == false && count != buf.length) {
|
||||
throw new IllegalStateException("Buffer not full");
|
||||
}
|
||||
|
||||
if (blockNum == 0 && config.isUniqueIV()) {
|
||||
out.write(this.fileHeader);
|
||||
}
|
||||
|
||||
byte[] encBuffer;
|
||||
try {
|
||||
if (count == buf.length) {
|
||||
encBuffer = EncFSCrypto.blockEncode(volume, getBlockIV(), buf);
|
||||
} else {
|
||||
// copy the buffer as stream encode needs the sub-range & it
|
||||
// also modifies it
|
||||
byte[] tmpBuf = Arrays.copyOf(buf, count);
|
||||
|
||||
encBuffer = EncFSCrypto.streamEncode(volume, getBlockIV(), tmpBuf);
|
||||
}
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new IOException(e);
|
||||
} catch (BadPaddingException e) {
|
||||
throw new IOException(e);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
throw new IOException(e);
|
||||
} catch (EncFSUnsupportedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
out.write(encBuffer);
|
||||
count = 0;
|
||||
blockNum++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the block IV for the current block
|
||||
*/
|
||||
private byte[] getBlockIV() {
|
||||
long fileIvLong = EncFSUtil.byteArrayToLong(fileIv);
|
||||
return EncFSUtil.longToByteArray(blockNum ^ fileIvLong);
|
||||
}
|
||||
|
||||
/** Flush the internal buffer */
|
||||
private void writeBuffer() throws IOException {
|
||||
writeBuffer(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(int b) throws IOException {
|
||||
buf[count++] = (byte) b;
|
||||
|
||||
if (count == buf.length) {
|
||||
writeBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(byte b[], int off, int len) throws IOException {
|
||||
if (count + len <= buf.length) {
|
||||
System.arraycopy(b, off, buf, count, len);
|
||||
count += len;
|
||||
|
||||
if (count == buf.length) {
|
||||
writeBuffer();
|
||||
}
|
||||
} else {
|
||||
int tmpOff = off;
|
||||
int remaining = len;
|
||||
while (remaining > 0) {
|
||||
int chunk = Math.min(remaining, buf.length - count);
|
||||
|
||||
write(b, tmpOff, chunk);
|
||||
|
||||
remaining -= chunk;
|
||||
tmpOff += chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
writeBuffer(true);
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
public EncFSFileOutputStream(EncFSFile encfsFile) throws IOException, EncFSUnsupportedException,
|
||||
EncFSCorruptDataException {
|
||||
if (!encfsFile.getFile().exists()) {
|
||||
encfsFile.getFile().createNewFile();
|
||||
}
|
||||
|
||||
FileOutputStream fop = new FileOutputStream(encfsFile.getFile());
|
||||
this.encfsOs = new EncFSBufferedOutputStream(encfsFile.getVolume(), fop);
|
||||
}
|
||||
|
||||
public EncFSFileOutputStream(EncFSVolume volume, OutputStream outputStream) throws EncFSUnsupportedException,
|
||||
EncFSCorruptDataException {
|
||||
this.encfsOs = new EncFSBufferedOutputStream(volume, outputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
encfsOs.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b) throws IOException {
|
||||
encfsOs.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
encfsOs.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
encfsOs.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
encfsOs.flush();
|
||||
}
|
||||
|
||||
}
|
|
@ -396,4 +396,19 @@ public class EncFSVolume {
|
|||
public EncFSFile getRootDir() {
|
||||
return rootDir;
|
||||
}
|
||||
|
||||
public EncFSFile getEncFSFile(String volumePath, String fileName) throws EncFSCorruptDataException,
|
||||
EncFSChecksumException {
|
||||
if (volumePath.startsWith("/") == false) {
|
||||
throw new IllegalArgumentException("Volume path must be absolute");
|
||||
}
|
||||
|
||||
String toEncFileName = EncFSCrypto.encodeName(this, fileName, volumePath);
|
||||
String toEncVolumePath = EncFSCrypto.encodePath(this, volumePath.substring(1), "/");
|
||||
|
||||
File rawEncFile = new File(this.getRootDir().getFile().getAbsolutePath() + "/" + toEncVolumePath, toEncFileName);
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
return new EncFSFile(this, volumePath, rawEncFile);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
package org.mrpdaemon.sec.encfs;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
|
@ -47,8 +53,8 @@ public class EncFSComparer {
|
|||
this.decodedEncFSOutput = decodedEncFSOutput;
|
||||
}
|
||||
|
||||
private int compare() throws FileNotFoundException, EncFSInvalidPasswordException, EncFSInvalidConfigException,
|
||||
EncFSCorruptDataException, EncFSUnsupportedException, EncFSChecksumException {
|
||||
private int compare() throws EncFSInvalidPasswordException, EncFSInvalidConfigException, EncFSCorruptDataException,
|
||||
EncFSUnsupportedException, EncFSChecksumException, IOException {
|
||||
logger.info("Performing compare between encfs raw volume at {} and output files at {}", rawEncFSVolume,
|
||||
decodedEncFSOutput);
|
||||
|
||||
|
@ -65,7 +71,8 @@ public class EncFSComparer {
|
|||
return result;
|
||||
}
|
||||
|
||||
private int compare(EncFSFile encFsDir, File decodedFsDir) throws EncFSCorruptDataException, EncFSChecksumException {
|
||||
private int compare(EncFSFile encFsDir, File decodedFsDir) throws EncFSCorruptDataException,
|
||||
EncFSChecksumException, EncFSUnsupportedException, IOException {
|
||||
logger.info("Comparing directory {}", decodedFsDir.getAbsoluteFile());
|
||||
|
||||
EncFSFile[] encFsFiles = encFsDir.listFiles();
|
||||
|
@ -94,7 +101,7 @@ public class EncFSComparer {
|
|||
if (rawFileName.equals(reEncEncfsName) == false) {
|
||||
logger.error("Re-encoded name miss match ({}, {}, {}, {})", new Object[] { i, encFsFile.getName(),
|
||||
rawFileName, reEncEncfsName });
|
||||
// return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (encFsFile.getFile().lastModified() != decodedFsFile.lastModified()) {
|
||||
|
@ -115,6 +122,73 @@ public class EncFSComparer {
|
|||
if (subResult != 0) {
|
||||
return subResult;
|
||||
}
|
||||
} else {
|
||||
// Check that the EncFSFileInputStream reads the file the
|
||||
// same as
|
||||
// reading the file directly from the mounted encfs volume
|
||||
|
||||
EncFSFileInputStream encfsIs = new EncFSFileInputStream(encFsFile);
|
||||
try {
|
||||
BufferedInputStream decFsIs = new BufferedInputStream(new FileInputStream(decodedFsFile));
|
||||
String decodedFsFileName = decodedFsFile.getAbsoluteFile().getName();
|
||||
try {
|
||||
int streamresult = compareInputStreams(encfsIs, decFsIs, decodedFsFileName);
|
||||
if (streamresult != 0) {
|
||||
return streamresult;
|
||||
}
|
||||
} finally {
|
||||
decFsIs.close();
|
||||
}
|
||||
} finally {
|
||||
encfsIs.close();
|
||||
}
|
||||
|
||||
// Copy the file via input/output streams & then check that
|
||||
// the file is the same
|
||||
File t = File.createTempFile(this.getClass().getName(), ".tmp");
|
||||
try {
|
||||
EncFSFileOutputStream efos = new EncFSFileOutputStream(encFsDir.getVolume(),
|
||||
new BufferedOutputStream(new FileOutputStream(t)));
|
||||
try {
|
||||
EncFSFileInputStream efis = new EncFSFileInputStream(encFsFile);
|
||||
try {
|
||||
int bytesRead = 0;
|
||||
while (bytesRead >= 0) {
|
||||
byte[] readBuf = new byte[(int) (encFsFile.getVolume().getConfig().getBlockSize() * 0.75)];
|
||||
bytesRead = efis.read(readBuf);
|
||||
if (bytesRead >= 0) {
|
||||
efos.write(readBuf, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
efis.close();
|
||||
}
|
||||
|
||||
} finally {
|
||||
efos.close();
|
||||
}
|
||||
|
||||
FileInputStream reEncFSIs = new FileInputStream(t);
|
||||
try {
|
||||
FileInputStream origEncFSIs = new FileInputStream(encFsFile.getFile());
|
||||
try {
|
||||
int streamresult = compareInputStreams(origEncFSIs, reEncFSIs, encFsFile.getFile()
|
||||
.getAbsoluteFile().getName());
|
||||
if (streamresult != 0) {
|
||||
return streamresult;
|
||||
}
|
||||
} finally {
|
||||
origEncFSIs.close();
|
||||
}
|
||||
} finally {
|
||||
reEncFSIs.close();
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (t.exists()) {
|
||||
t.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,6 +196,30 @@ public class EncFSComparer {
|
|||
return 0;
|
||||
}
|
||||
|
||||
private int compareInputStreams(InputStream encfsIs, InputStream decFsIs, String decodedFsFileName)
|
||||
throws IOException {
|
||||
int bytesRead = 0, bytesRead2 = 0;
|
||||
while (bytesRead >= 0) {
|
||||
byte[] readBuf = new byte[128];
|
||||
byte[] readBuf2 = new byte[128];
|
||||
|
||||
bytesRead = encfsIs.read(readBuf);
|
||||
bytesRead2 = decFsIs.read(readBuf2);
|
||||
|
||||
if (bytesRead != bytesRead2) {
|
||||
logger.error("File bytes read missmatch {} ({}, {})", new Object[] { decodedFsFileName, bytesRead,
|
||||
bytesRead2 });
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (Arrays.equals(readBuf, readBuf2) == false) {
|
||||
logger.error("File bytes missmatch {}", decodedFsFileName);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static class SimpleEncFSFileComparator implements Comparator<EncFSFile> {
|
||||
private static final SimpleEncFSFileComparator instance = new SimpleEncFSFileComparator();
|
||||
|
||||
|
|
|
@ -32,4 +32,25 @@ public class EncFSCryptoTest {
|
|||
Assert.assertArrayEquals(orig, b2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamEncodeDecode2() throws EncFSInvalidPasswordException, EncFSInvalidConfigException,
|
||||
EncFSCorruptDataException, EncFSUnsupportedException, EncFSChecksumException, IOException,
|
||||
InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
|
||||
File encFSDir = new File("test/encfs_samples/boxcryptor_1");
|
||||
Assert.assertTrue(encFSDir.exists());
|
||||
|
||||
String password = "test";
|
||||
EncFSVolume volume = new EncFSVolume(encFSDir, password);
|
||||
|
||||
String str = "test file\r";
|
||||
|
||||
byte[] orig = str.getBytes();
|
||||
byte[] ivSeed = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
byte[] b1 = EncFSCrypto.streamEncode(volume, ivSeed, Arrays.copyOf(orig, orig.length));
|
||||
byte[] b2 = EncFSCrypto.streamDecode(volume, ivSeed, Arrays.copyOf(b1, b1.length));
|
||||
|
||||
Assert.assertArrayEquals(orig, b2);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package org.mrpdaemon.sec.encfs;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
|
@ -43,6 +47,7 @@ public class EncFSVolumeTest {
|
|||
Assert.fail();
|
||||
} catch (EncFSInvalidPasswordException e) {
|
||||
// this is correct that we should have got this exception
|
||||
Assert.assertNotNull(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +68,10 @@ public class EncFSVolumeTest {
|
|||
Assert.assertEquals("test.txt", encFSFile.getName());
|
||||
|
||||
String contents = readInputStreamAsString(encFSFile);
|
||||
Assert.assertEquals("This is a test file.", contents);
|
||||
Assert.assertEquals("This is a test file.\n", contents);
|
||||
|
||||
assertFileNameEncoding(rootDir);
|
||||
assertEncFSFileRoundTrip(rootDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -83,7 +91,10 @@ public class EncFSVolumeTest {
|
|||
Assert.assertEquals("testfile.txt", encFSFile.getName());
|
||||
|
||||
String contents = readInputStreamAsString(encFSFile);
|
||||
Assert.assertEquals("Test file for non-unique-IV file.", contents);
|
||||
Assert.assertEquals("Test file for non-unique-IV file.\n", contents);
|
||||
|
||||
assertFileNameEncoding(rootDir);
|
||||
assertEncFSFileRoundTrip(rootDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -103,7 +114,10 @@ public class EncFSVolumeTest {
|
|||
Assert.assertEquals("testfile.txt", encFSFile.getName());
|
||||
|
||||
String contents = readInputStreamAsString(encFSFile);
|
||||
Assert.assertEquals("test file\r", contents);
|
||||
Assert.assertEquals("test file\r\n", contents);
|
||||
|
||||
assertFileNameEncoding(rootDir);
|
||||
assertEncFSFileRoundTrip(rootDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -127,14 +141,17 @@ public class EncFSVolumeTest {
|
|||
Assert.assertEquals("file1.txt", encFSFile.getName());
|
||||
|
||||
String contents = readInputStreamAsString(encFSFile);
|
||||
Assert.assertEquals("Some contents for file", contents);
|
||||
Assert.assertEquals("Some contents for file1", contents);
|
||||
|
||||
String dirListing = GetDirListing(rootDir, true);
|
||||
String dirListing = getDirListing(rootDir, true);
|
||||
String expectedListing = "";
|
||||
expectedListing += "/Dir1" + "\n";
|
||||
expectedListing += "/Dir1/file2.txt" + "\n";
|
||||
expectedListing += "/file1.txt";
|
||||
Assert.assertEquals(expectedListing, dirListing);
|
||||
|
||||
assertFileNameEncoding(rootDir);
|
||||
assertEncFSFileRoundTrip(rootDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -149,38 +166,120 @@ public class EncFSVolumeTest {
|
|||
EncFSFile[] files = rootDir.listFiles();
|
||||
Assert.assertEquals(1, files.length);
|
||||
|
||||
String dirListing = GetDirListing(rootDir, true);
|
||||
String dirListing = getDirListing(rootDir, true);
|
||||
Assert.assertNotNull(dirListing);
|
||||
|
||||
assertFileNameEncoding(rootDir);
|
||||
assertEncFSFileRoundTrip(rootDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBoxCryptor_1_encode() throws EncFSInvalidPasswordException, EncFSInvalidConfigException,
|
||||
EncFSCorruptDataException, EncFSUnsupportedException, EncFSChecksumException, IOException {
|
||||
File encFSDir = new File("test/encfs_samples/boxcryptor_1");
|
||||
Assert.assertTrue(encFSDir.exists());
|
||||
private void assertFileNameEncoding(EncFSFile encfsFileDir) throws EncFSCorruptDataException,
|
||||
EncFSChecksumException {
|
||||
for (EncFSFile encfFile : encfsFileDir.listFiles()) {
|
||||
EncFSVolume volume = encfsFileDir.getVolume();
|
||||
String decName = EncFSCrypto.decodeName(volume, encfFile.getFile().getName(), encfFile.getVolumePath());
|
||||
Assert.assertEquals(encfFile.getAbsoluteName() + " decoded file name", encfFile.getName(), decName);
|
||||
|
||||
String password = "test";
|
||||
EncFSVolume volume = new EncFSVolume(encFSDir, password);
|
||||
String encName = EncFSCrypto.encodeName(volume, decName, encfFile.getVolumePath());
|
||||
Assert.assertEquals(encfFile.getAbsoluteName() + " re-encoded file name", encfFile.getFile().getName(),
|
||||
encName);
|
||||
|
||||
String fileName = "W3gLoUqL-0YzUh8udP8";
|
||||
String volumePath = "/";
|
||||
|
||||
String decName = EncFSCrypto.decodeName(volume, fileName, volumePath);
|
||||
Assert.assertEquals("testfile.txt", decName);
|
||||
|
||||
String encName = EncFSCrypto.encodeName(volume, decName, volumePath);
|
||||
Assert.assertEquals(fileName, encName);
|
||||
if (encfFile.isDirectory()) {
|
||||
assertFileNameEncoding(encfFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String GetDirListing(EncFSFile rootDir, boolean recursive) throws EncFSCorruptDataException,
|
||||
private void assertEncFSFileRoundTrip(EncFSFile encFsFile) throws IOException, EncFSUnsupportedException,
|
||||
EncFSCorruptDataException, EncFSChecksumException {
|
||||
if (encFsFile.isDirectory() == false) {
|
||||
// Copy the file via input/output streams & then check that
|
||||
// the file is the same
|
||||
File t = File.createTempFile(this.getClass().getName(), ".tmp");
|
||||
try {
|
||||
EncFSFileOutputStream efos = new EncFSFileOutputStream(encFsFile.getVolume(), new BufferedOutputStream(
|
||||
new FileOutputStream(t)));
|
||||
try {
|
||||
EncFSFileInputStream efis = new EncFSFileInputStream(encFsFile);
|
||||
try {
|
||||
int bytesRead = 0;
|
||||
while (bytesRead >= 0) {
|
||||
byte[] readBuf = new byte[(int) (encFsFile.getVolume().getConfig().getBlockSize() * 0.75)];
|
||||
bytesRead = efis.read(readBuf);
|
||||
if (bytesRead >= 0) {
|
||||
efos.write(readBuf, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
efis.close();
|
||||
}
|
||||
|
||||
} finally {
|
||||
efos.close();
|
||||
}
|
||||
|
||||
if (encFsFile.getVolume().getConfig().isUniqueIV() == false) {
|
||||
FileInputStream reEncFSIs = new FileInputStream(t);
|
||||
try {
|
||||
FileInputStream origEncFSIs = new FileInputStream(encFsFile.getFile());
|
||||
try {
|
||||
assertInputStreamsAreEqual(encFsFile.getAbsoluteName(), origEncFSIs, reEncFSIs);
|
||||
} finally {
|
||||
origEncFSIs.close();
|
||||
}
|
||||
} finally {
|
||||
reEncFSIs.close();
|
||||
}
|
||||
} else {
|
||||
EncFSFileInputStream efis = new EncFSFileInputStream(encFsFile);
|
||||
try {
|
||||
EncFSFileInputStream efisCopy = new EncFSFileInputStream(encFsFile.getVolume(),
|
||||
new FileInputStream(t));
|
||||
try {
|
||||
assertInputStreamsAreEqual(encFsFile.getAbsoluteName(), efis, efisCopy);
|
||||
} finally {
|
||||
efisCopy.close();
|
||||
}
|
||||
} finally {
|
||||
efis.close();
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
if (t.exists()) {
|
||||
t.delete();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (EncFSFile subEncfFile : encFsFile.listFiles()) {
|
||||
assertEncFSFileRoundTrip(subEncfFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void assertInputStreamsAreEqual(String msg, InputStream encfsIs, InputStream decFsIs) throws IOException {
|
||||
int bytesRead = 0, bytesRead2 = 0;
|
||||
while (bytesRead >= 0) {
|
||||
byte[] readBuf = new byte[128];
|
||||
byte[] readBuf2 = new byte[128];
|
||||
|
||||
bytesRead = encfsIs.read(readBuf);
|
||||
bytesRead2 = decFsIs.read(readBuf2);
|
||||
|
||||
Assert.assertEquals(msg, bytesRead, bytesRead2);
|
||||
Assert.assertArrayEquals(msg, readBuf, readBuf2);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getDirListing(EncFSFile rootDir, boolean recursive) throws EncFSCorruptDataException,
|
||||
EncFSChecksumException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
GetDirListing(rootDir, recursive, sb);
|
||||
getDirListing(rootDir, recursive, sb);
|
||||
return sb.toString();
|
||||
|
||||
}
|
||||
|
||||
private static void GetDirListing(EncFSFile rootDir, boolean recursive, StringBuilder sb)
|
||||
private static void getDirListing(EncFSFile rootDir, boolean recursive, StringBuilder sb)
|
||||
throws EncFSCorruptDataException, EncFSChecksumException {
|
||||
|
||||
for (EncFSFile encFile : rootDir.listFiles()) {
|
||||
|
@ -193,7 +292,7 @@ public class EncFSVolumeTest {
|
|||
}
|
||||
sb.append(encFile.getName());
|
||||
if (encFile.isDirectory() && recursive) {
|
||||
GetDirListing(encFile, recursive, sb);
|
||||
getDirListing(encFile, recursive, sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -219,4 +318,27 @@ public class EncFSVolumeTest {
|
|||
return new String(buf.toByteArray());
|
||||
}
|
||||
|
||||
public static void copyViaStreams(EncFSFile srcEncFSFile, EncFSFile targetEncFSFile) throws IOException,
|
||||
EncFSCorruptDataException, EncFSUnsupportedException {
|
||||
|
||||
EncFSFileOutputStream efos = new EncFSFileOutputStream(targetEncFSFile);
|
||||
try {
|
||||
EncFSFileInputStream efis = new EncFSFileInputStream(srcEncFSFile);
|
||||
try {
|
||||
int bytesRead = 0;
|
||||
while (bytesRead >= 0) {
|
||||
byte[] readBuf = new byte[128];
|
||||
bytesRead = efis.read(readBuf);
|
||||
if (bytesRead >= 0) {
|
||||
efos.write(readBuf, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
efis.close();
|
||||
}
|
||||
|
||||
} finally {
|
||||
efos.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue