|
@@ -47,6 +47,7 @@ import org.bouncycastle.crypto.generators.SCrypt;
|
|
|
import org.cryptomator.crypto.AbstractCryptor;
|
|
|
import org.cryptomator.crypto.CryptorIOSupport;
|
|
|
import org.cryptomator.crypto.exceptions.DecryptFailedException;
|
|
|
+import org.cryptomator.crypto.exceptions.MacAuthenticationFailedException;
|
|
|
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
|
|
|
import org.cryptomator.crypto.exceptions.WrongPasswordException;
|
|
|
import org.cryptomator.crypto.io.SeekableByteChannelInputStream;
|
|
@@ -369,34 +370,6 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
|
|
|
ioSupport.writePathSpecificMetadata(metadataFile, objectMapper.writeValueAsBytes(metadata));
|
|
|
}
|
|
|
|
|
|
- private void authenticateContent(SeekableByteChannel encryptedFile) throws IOException, DecryptFailedException {
|
|
|
-
|
|
|
- final Mac calculatedMac = this.hmacSha256(hMacMasterKey);
|
|
|
-
|
|
|
-
|
|
|
- encryptedFile.position(16);
|
|
|
- final ByteBuffer storedMac = ByteBuffer.allocate(calculatedMac.getMacLength());
|
|
|
- final int numMacBytesRead = encryptedFile.read(storedMac);
|
|
|
-
|
|
|
-
|
|
|
- if (numMacBytesRead != calculatedMac.getMacLength()) {
|
|
|
- throw new IOException("Failed to read file header.");
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- encryptedFile.position(64);
|
|
|
- final InputStream in = new SeekableByteChannelInputStream(encryptedFile);
|
|
|
- final InputStream macIn = new MacInputStream(in, calculatedMac);
|
|
|
- IOUtils.copyLarge(macIn, new NullOutputStream());
|
|
|
-
|
|
|
-
|
|
|
- boolean macMatches = MessageDigest.isEqual(storedMac.array(), calculatedMac.doFinal());
|
|
|
-
|
|
|
- if (!macMatches) {
|
|
|
- throw new DecryptFailedException("MAC authentication failed.");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
@Override
|
|
|
public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException {
|
|
|
|
|
@@ -422,24 +395,49 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private void encryptedContentLength(SeekableByteChannel encryptedFile, Long contentLength) throws IOException {
|
|
|
+ final ByteBuffer encryptedFileSizeBuffer;
|
|
|
+
|
|
|
+
|
|
|
+ try {
|
|
|
+ final ByteBuffer fileSizeBuffer = ByteBuffer.allocate(Long.BYTES);
|
|
|
+ fileSizeBuffer.putLong(contentLength);
|
|
|
+ final Cipher sizeCipher = aesEcbCipher(primaryMasterKey, Cipher.ENCRYPT_MODE);
|
|
|
+ final byte[] encryptedFileSize = sizeCipher.doFinal(fileSizeBuffer.array());
|
|
|
+ encryptedFileSizeBuffer = ByteBuffer.wrap(encryptedFileSize);
|
|
|
+ } catch (IllegalBlockSizeException | BadPaddingException e) {
|
|
|
+ throw new IllegalStateException("Block size must be valid, as padding is requested. BadPaddingException not possible in encrypt mode.", e);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ encryptedFile.position(48);
|
|
|
+
|
|
|
+
|
|
|
+ encryptedFile.write(encryptedFileSizeBuffer);
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
- public Long decryptedFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile) throws IOException, DecryptFailedException {
|
|
|
+ public Long decryptFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile) throws IOException, DecryptFailedException {
|
|
|
|
|
|
encryptedFile.position(0);
|
|
|
final ByteBuffer countingIv = ByteBuffer.allocate(AES_BLOCK_LENGTH);
|
|
|
final int numIvBytesRead = encryptedFile.read(countingIv);
|
|
|
|
|
|
+
|
|
|
+ final Mac calculatedMac = this.hmacSha256(hMacMasterKey);
|
|
|
+
|
|
|
+
|
|
|
+ final ByteBuffer storedMac = ByteBuffer.allocate(calculatedMac.getMacLength());
|
|
|
+ final int numMacBytesRead = encryptedFile.read(storedMac);
|
|
|
+
|
|
|
|
|
|
final Long fileSize = decryptedContentLength(encryptedFile);
|
|
|
|
|
|
|
|
|
- if (numIvBytesRead != AES_BLOCK_LENGTH || fileSize == null) {
|
|
|
+ if (numIvBytesRead != AES_BLOCK_LENGTH || numMacBytesRead != calculatedMac.getMacLength() || fileSize == null) {
|
|
|
throw new IOException("Failed to read file header.");
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- this.authenticateContent(encryptedFile);
|
|
|
-
|
|
|
|
|
|
encryptedFile.position(64);
|
|
|
|
|
@@ -448,8 +446,25 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
|
|
|
|
|
|
|
|
|
final InputStream in = new SeekableByteChannelInputStream(encryptedFile);
|
|
|
- final InputStream cipheredIn = new CipherInputStream(in, cipher);
|
|
|
- return IOUtils.copyLarge(cipheredIn, plaintextFile, 0, fileSize);
|
|
|
+ final InputStream macIn = new MacInputStream(in, calculatedMac);
|
|
|
+ final InputStream cipheredIn = new CipherInputStream(macIn, cipher);
|
|
|
+ final long bytesDecrypted = IOUtils.copyLarge(cipheredIn, plaintextFile, 0, fileSize);
|
|
|
+
|
|
|
+
|
|
|
+ IOUtils.copyLarge(macIn, new NullOutputStream());
|
|
|
+
|
|
|
+
|
|
|
+ final boolean macMatches = MessageDigest.isEqual(storedMac.array(), calculatedMac.doFinal());
|
|
|
+ if (!macMatches) {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ throw new MacAuthenticationFailedException("MAC authentication failed.");
|
|
|
+ }
|
|
|
+
|
|
|
+ return bytesDecrypted;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -464,9 +479,6 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
|
|
|
throw new IOException("Failed to read file header.");
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- this.authenticateContent(encryptedFile);
|
|
|
-
|
|
|
|
|
|
long firstRelevantBlock = pos / AES_BLOCK_LENGTH;
|
|
|
long beginOfFirstRelevantBlock = firstRelevantBlock * AES_BLOCK_LENGTH;
|
|
@@ -493,7 +505,6 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
|
|
|
|
|
|
final ByteBuffer countingIv = ByteBuffer.wrap(randomData(AES_BLOCK_LENGTH));
|
|
|
countingIv.putLong(AES_BLOCK_LENGTH - Long.BYTES, 0l);
|
|
|
- countingIv.position(0);
|
|
|
encryptedFile.write(countingIv);
|
|
|
|
|
|
|
|
@@ -504,9 +515,8 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
|
|
|
final ByteBuffer macBuffer = ByteBuffer.allocate(mac.getMacLength());
|
|
|
encryptedFile.write(macBuffer);
|
|
|
|
|
|
-
|
|
|
- final ByteBuffer encryptedFileSizeBuffer = ByteBuffer.allocate(AES_BLOCK_LENGTH);
|
|
|
- encryptedFile.write(encryptedFileSizeBuffer);
|
|
|
+
|
|
|
+ encryptedContentLength(encryptedFile, 0l);
|
|
|
|
|
|
|
|
|
final OutputStream out = new SeekableByteChannelOutputStream(encryptedFile);
|
|
@@ -529,26 +539,14 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
|
|
|
blockSizeBufferedOut.flush();
|
|
|
|
|
|
|
|
|
- macBuffer.position(0);
|
|
|
+ macBuffer.clear();
|
|
|
macBuffer.put(mac.doFinal());
|
|
|
- macBuffer.position(0);
|
|
|
+ macBuffer.flip();
|
|
|
encryptedFile.position(16);
|
|
|
encryptedFile.write(macBuffer);
|
|
|
|
|
|
-
|
|
|
- try {
|
|
|
- final ByteBuffer fileSizeBuffer = ByteBuffer.allocate(Long.BYTES);
|
|
|
- fileSizeBuffer.putLong(plaintextSize);
|
|
|
- final Cipher sizeCipher = aesEcbCipher(primaryMasterKey, Cipher.ENCRYPT_MODE);
|
|
|
- final byte[] encryptedFileSize = sizeCipher.doFinal(fileSizeBuffer.array());
|
|
|
- encryptedFileSizeBuffer.position(0);
|
|
|
- encryptedFileSizeBuffer.put(encryptedFileSize);
|
|
|
- encryptedFileSizeBuffer.position(0);
|
|
|
- encryptedFile.position(48);
|
|
|
- encryptedFile.write(encryptedFileSizeBuffer);
|
|
|
- } catch (IllegalBlockSizeException | BadPaddingException e) {
|
|
|
- throw new IllegalStateException("Block size must be valid, as padding is requested. BadPaddingException not possible in encrypt mode.", e);
|
|
|
- }
|
|
|
+
|
|
|
+ encryptedContentLength(encryptedFile, plaintextSize);
|
|
|
|
|
|
return plaintextSize;
|
|
|
}
|