mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 02:01:27 +00:00
- added file integrity check (#17) - not yet visible to the user
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user