- added file integrity check (#17) - not yet visible to the user

This commit is contained in:
Sebastian Stenzel
2015-01-06 11:39:31 +01:00
parent e19cf1c942
commit 2e67910a60
3 changed files with 47 additions and 12 deletions

View File

@@ -41,6 +41,7 @@ import javax.security.auth.Destroyable;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.crypto.generators.SCrypt;
@@ -403,7 +404,30 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
@Override
public boolean authenticateContent(SeekableByteChannel encryptedFile) throws IOException {
throw new UnsupportedOperationException("Not yet implemented.");
// read file size:
final Long fileSize = decryptedContentLength(encryptedFile);
// init mac:
final Mac mac = this.hmacSha256(hMacMasterKey);
// read stored mac:
encryptedFile.position(16);
final ByteBuffer macBuffer = ByteBuffer.allocate(mac.getMacLength());
final int numMacBytesRead = encryptedFile.read(macBuffer);
// check validity of header:
if (numMacBytesRead != mac.getMacLength() || fileSize == null) {
throw new IOException("Failed to read file header.");
}
// read all encrypted data and calculate mac:
encryptedFile.position(64);
final InputStream in = new SeekableByteChannelInputStream(encryptedFile);
final InputStream macIn = new MacInputStream(in, mac);
IOUtils.copyLarge(macIn, new NullOutputStream(), 0, fileSize);
// compare:
return Arrays.equals(macBuffer.array(), mac.doFinal());
}
@Override
@@ -517,18 +541,18 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
final OutputStream cipheredOut = new CipherOutputStream(macOut, cipher);
final Long actualSize = IOUtils.copyLarge(plaintextFile, cipheredOut);
// copy MAC:
macBuffer.position(0);
macBuffer.put(mac.doFinal());
// append fake content:
final int randomContentLength = (int) Math.ceil(Math.random() * actualSize / 10.0);
final int randomContentLength = (int) Math.ceil((Math.random() + 1.0) * actualSize / 20.0);
final byte[] emptyBytes = new byte[AES_BLOCK_LENGTH];
for (int i = 0; i < randomContentLength; i += AES_BLOCK_LENGTH) {
cipheredOut.write(emptyBytes);
}
cipheredOut.flush();
// copy MAC:
macBuffer.position(0);
macBuffer.put(mac.doFinal());
// encrypt actualSize
try {
final ByteBuffer fileSizeBuffer = ByteBuffer.allocate(Long.BYTES);

View File

@@ -32,7 +32,7 @@ class MacInputStream extends FilterInputStream {
@Override
public int read(byte[] b, int off, int len) throws IOException {
int read = in.read(b, off, len);
mac.update(b);
mac.update(b, off, len);
return read;
}

View File

@@ -25,7 +25,6 @@ import org.cryptomator.crypto.exceptions.DecryptFailedException;
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
import org.cryptomator.crypto.exceptions.WrongPasswordException;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
public class Aes256CryptorTest {
@@ -73,7 +72,6 @@ public class Aes256CryptorTest {
}
}
@Ignore
@Test
public void testIntegrityAuthentication() throws IOException {
// our test plaintext data:
@@ -93,9 +91,22 @@ public class Aes256CryptorTest {
encryptedData.position(0);
// authenticate unmodified content:
final SeekableByteChannel encryptedIn = new ByteBufferBackedSeekableChannel(encryptedData);
final boolean unmodifiedContent = cryptor.authenticateContent(encryptedIn);
Assert.assertTrue(unmodifiedContent);
final SeekableByteChannel encryptedIn1 = new ByteBufferBackedSeekableChannel(encryptedData);
final boolean isContentUnmodified1 = cryptor.authenticateContent(encryptedIn1);
Assert.assertTrue(isContentUnmodified1);
// toggle one bit inf first content byte:
encryptedData.position(64);
final byte fifthByte = encryptedData.get();
encryptedData.position(64);
encryptedData.put((byte) (fifthByte ^ 0x01));
encryptedData.position(0);
// authenticate modified content:
final SeekableByteChannel encryptedIn2 = new ByteBufferBackedSeekableChannel(encryptedData);
final boolean isContentUnmodified2 = cryptor.authenticateContent(encryptedIn2);
Assert.assertFalse(isContentUnmodified2);
}
@Test