diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFile.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFile.java index be76d711f..30a3cd160 100644 --- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFile.java +++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFile.java @@ -107,6 +107,9 @@ class EncryptedFile extends AbstractEncryptedNode { } catch (IOException e) { LOG.error("Error reading filesize " + path.toString(), e); throw new IORuntimeException(e); + } catch (MacAuthenticationFailedException e) { + LOG.warn("Content length couldn't be determined due to MAC authentication violation."); + // don't add content length DAV property } try { diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java index fc9e1b77a..e6ebe8667 100644 --- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java +++ b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java @@ -60,8 +60,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, FileNamingConventions { /** - * Defined in static initializer. Defaults to 256, but falls back to maximum value possible, if JCE Unlimited Strength Jurisdiction - * Policy Files isn't installed. Those files can be downloaded here: http://www.oracle.com/technetwork/java/javase/downloads/. + * Defined in static initializer. Defaults to 256, but falls back to maximum value possible, if JCE Unlimited Strength Jurisdiction Policy Files isn't installed. Those files can be downloaded + * here: http://www.oracle.com/technetwork/java/javase/downloads/. */ private static final int AES_KEY_LENGTH_IN_BITS; @@ -78,8 +78,8 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi private final ObjectMapper objectMapper = new ObjectMapper(); /** - * The decrypted master key. Its lifecycle starts with the construction of an Aes256Cryptor instance or - * {@link #decryptMasterKey(InputStream, CharSequence)}. Its lifecycle ends with {@link #swipeSensitiveData()}. + * The decrypted master key. Its lifecycle starts with the construction of an Aes256Cryptor instance or {@link #decryptMasterKey(InputStream, CharSequence)}. Its lifecycle ends with + * {@link #swipeSensitiveData()}. */ private SecretKey primaryMasterKey; @@ -149,10 +149,8 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi * Reads the encrypted masterkey from the given input stream and decrypts it with the given password. * * @throws DecryptFailedException If the decryption failed for various reasons (including wrong password). - * @throws WrongPasswordException If the provided password was wrong. Note: Sometimes the algorithm itself fails due to a wrong - * password. In this case a DecryptFailedException will be thrown. - * @throws UnsupportedKeyLengthException If the masterkey has been encrypted with a higher key length than supported by the system. In - * this case Java JCE needs to be installed. + * @throws WrongPasswordException If the provided password was wrong. Note: Sometimes the algorithm itself fails due to a wrong password. In this case a DecryptFailedException will be thrown. + * @throws UnsupportedKeyLengthException If the masterkey has been encrypted with a higher key length than supported by the system. In this case Java JCE needs to be installed. */ @Override public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException { @@ -293,18 +291,14 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi /** * Each path component, i.e. file or directory name separated by path separators, gets encrypted for its own.
- * Encryption will blow up the filename length due to aes block sizes and base32 encoding. The result may be too long for some old file - * systems.
- * This means that we need a workaround for filenames longer than the limit defined in - * {@link FileNamingConventions#ENCRYPTED_FILENAME_LENGTH_LIMIT}.
+ * Encryption will blow up the filename length due to aes block sizes and base32 encoding. The result may be too long for some old file systems.
+ * This means that we need a workaround for filenames longer than the limit defined in {@link FileNamingConventions#ENCRYPTED_FILENAME_LENGTH_LIMIT}.
*
- * In any case we will create the encrypted filename normally. For those, that are too long, we calculate a checksum. No - * cryptographically secure hash is needed here. We just want an uniform distribution for better load balancing. All encrypted filenames - * with the same checksum will then share a metadata file, in which a lookup map between encrypted filenames and short unique + * In any case we will create the encrypted filename normally. For those, that are too long, we calculate a checksum. No cryptographically secure hash is needed here. We just want an uniform + * distribution for better load balancing. All encrypted filenames with the same checksum will then share a metadata file, in which a lookup map between encrypted filenames and short unique * alternative names are stored.
*
- * These alternative names consist of the checksum, a unique id and a special file extension defined in - * {@link FileNamingConventions#LONG_NAME_FILE_EXT}. + * These alternative names consist of the checksum, a unique id and a special file extension defined in {@link FileNamingConventions#LONG_NAME_FILE_EXT}. */ @Override public String encryptFilename(String cleartextName, CryptorMetadataSupport ioSupport) throws IOException { @@ -361,23 +355,65 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi } @Override - public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException { - // skip 128bit IV + 256 bit MAC: - encryptedFile.position(48); - - // read encrypted value: - final ByteBuffer encryptedFileSizeBuffer = ByteBuffer.allocate(AES_BLOCK_LENGTH); - final int numFileSizeBytesRead = encryptedFile.read(encryptedFileSizeBuffer); - - // return "unknown" value, if EOF - if (numFileSizeBytesRead != encryptedFileSizeBuffer.capacity()) { + public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException, MacAuthenticationFailedException { + // read header: + encryptedFile.position(0); + final ByteBuffer headerBuf = ByteBuffer.allocate(64); + final int headerBytesRead = encryptedFile.read(headerBuf); + if (headerBytesRead != headerBuf.capacity()) { return null; } - // decrypt size: + // read content length: + headerBuf.position(16); + final byte[] encryptedContentLengthBytes = new byte[AES_BLOCK_LENGTH]; + headerBuf.get(encryptedContentLengthBytes); + final Long fileSize = decryptContentLength(encryptedContentLengthBytes); + + // read stored header mac: + headerBuf.position(32); + final byte[] storedHeaderMac = new byte[32]; + headerBuf.get(storedHeaderMac); + + // calculate mac over first 32 bytes of header: + final Mac headerMac = this.hmacSha256(hMacMasterKey); + headerBuf.rewind(); + headerBuf.limit(32); + headerMac.update(headerBuf); + + final boolean macMatches = MessageDigest.isEqual(storedHeaderMac, headerMac.doFinal()); + if (!macMatches) { + throw new MacAuthenticationFailedException("MAC authentication failed."); + } + + return fileSize; + } + + // private void encryptedContentLength(SeekableByteChannel encryptedFile, Long contentLength) throws IOException { + // final ByteBuffer encryptedFileSizeBuffer; + // + // // encrypt content length in ECB mode (content length is less than one block): + // 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); + // } + // + // // skip 128bit IV: + // encryptedFile.position(16l); + // + // // write result: + // encryptedFile.write(encryptedFileSizeBuffer); + // } + + private long decryptContentLength(byte[] encryptedContentLengthBytes) { try { final Cipher sizeCipher = aesEcbCipher(primaryMasterKey, Cipher.DECRYPT_MODE); - final byte[] decryptedFileSize = sizeCipher.doFinal(encryptedFileSizeBuffer.array()); + final byte[] decryptedFileSize = sizeCipher.doFinal(encryptedContentLengthBytes); final ByteBuffer fileSizeBuffer = ByteBuffer.wrap(decryptedFileSize); return fileSizeBuffer.getLong(); } catch (IllegalBlockSizeException | BadPaddingException e) { @@ -385,85 +421,90 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi } } - private void encryptedContentLength(SeekableByteChannel encryptedFile, Long contentLength) throws IOException { - final ByteBuffer encryptedFileSizeBuffer; - - // encrypt content length in ECB mode (content length is less than one block): + private byte[] encryptContentLength(long contentLength) { 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); + return sizeCipher.doFinal(fileSizeBuffer.array()); } catch (IllegalBlockSizeException | BadPaddingException e) { throw new IllegalStateException("Block size must be valid, as padding is requested. BadPaddingException not possible in encrypt mode.", e); } - - // skip 128bit IV + 256 bit MAC: - encryptedFile.position(48); - - // write result: - encryptedFile.write(encryptedFileSizeBuffer); } @Override public boolean isAuthentic(SeekableByteChannel encryptedFile) throws IOException { - // init mac: - final Mac calculatedMac = this.hmacSha256(hMacMasterKey); - - // read stored mac: - encryptedFile.position(16); - final ByteBuffer storedMac = ByteBuffer.allocate(calculatedMac.getMacLength()); - final int numMacBytesRead = encryptedFile.read(storedMac); - - // check validity of header: - if (numMacBytesRead != calculatedMac.getMacLength()) { + // read header: + encryptedFile.position(0); + final ByteBuffer headerBuf = ByteBuffer.allocate(96); + final int headerBytesRead = encryptedFile.read(headerBuf); + if (headerBytesRead != headerBuf.capacity()) { throw new IOException("Failed to read file header."); } - // go to begin of content: - encryptedFile.position(64); + // read header mac: + headerBuf.position(32); + final byte[] storedHeaderMac = new byte[32]; + headerBuf.get(storedHeaderMac); - // calculated MAC + // read content mac: + headerBuf.position(64); + final byte[] storedContentMac = new byte[32]; + headerBuf.get(storedContentMac); + + // calculate mac over first 32 bytes of header: + final Mac headerMac = this.hmacSha256(hMacMasterKey); + headerBuf.rewind(); + headerBuf.limit(32); + headerMac.update(headerBuf); + + // calculate mac over content: + encryptedFile.position(96); + final Mac contentMac = this.hmacSha256(hMacMasterKey); final InputStream in = new SeekableByteChannelInputStream(encryptedFile); - final InputStream macIn = new MacInputStream(in, calculatedMac); + final InputStream macIn = new MacInputStream(in, contentMac); IOUtils.copyLarge(macIn, new NullOutputStream()); // compare (in constant time): - return MessageDigest.isEqual(storedMac.array(), calculatedMac.doFinal()); + final boolean headerMacMatches = MessageDigest.isEqual(storedHeaderMac, headerMac.doFinal()); + final boolean contentMacMatches = MessageDigest.isEqual(storedContentMac, contentMac.doFinal()); + return headerMacMatches && contentMacMatches; } @Override public Long decryptFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile) throws IOException, DecryptFailedException { - // read iv: + // read header: encryptedFile.position(0); - final ByteBuffer countingIv = ByteBuffer.allocate(AES_BLOCK_LENGTH); - final int numIvBytesRead = encryptedFile.read(countingIv); - - // init mac: - final Mac calculatedMac = this.hmacSha256(hMacMasterKey); - - // read stored mac: - final ByteBuffer storedMac = ByteBuffer.allocate(calculatedMac.getMacLength()); - final int numMacBytesRead = encryptedFile.read(storedMac); - - // read file size: - final Long fileSize = decryptedContentLength(encryptedFile); - - // check validity of header: - if (numIvBytesRead != AES_BLOCK_LENGTH || numMacBytesRead != calculatedMac.getMacLength() || fileSize == null) { + final ByteBuffer headerBuf = ByteBuffer.allocate(96); + final int headerBytesRead = encryptedFile.read(headerBuf); + if (headerBytesRead != headerBuf.capacity()) { throw new IOException("Failed to read file header."); } + headerBuf.rewind(); - // go to begin of content: - encryptedFile.position(64); + // read iv: + final byte[] iv = new byte[AES_BLOCK_LENGTH]; + headerBuf.get(iv); - // generate cipher: - final Cipher cipher = this.aesCtrCipher(primaryMasterKey, countingIv.array(), Cipher.DECRYPT_MODE); + // read content length: + final byte[] encryptedContentLengthBytes = new byte[AES_BLOCK_LENGTH]; + headerBuf.get(encryptedContentLengthBytes); + final Long fileSize = decryptContentLength(encryptedContentLengthBytes); - // read content + // read header mac: + final byte[] headerMac = new byte[32]; + headerBuf.get(headerMac); + + // read content mac: + final byte[] contentMac = new byte[32]; + headerBuf.get(contentMac); + + // decrypt content + encryptedFile.position(96l); + final Mac calculatedContentMac = this.hmacSha256(hMacMasterKey); + final Cipher cipher = this.aesCtrCipher(primaryMasterKey, iv, Cipher.DECRYPT_MODE); final InputStream in = new SeekableByteChannelInputStream(encryptedFile); - final InputStream macIn = new MacInputStream(in, calculatedMac); + final InputStream macIn = new MacInputStream(in, calculatedContentMac); final InputStream cipheredIn = new CipherInputStream(macIn, cipher); final long bytesDecrypted = IOUtils.copyLarge(cipheredIn, plaintextFile, 0, fileSize); @@ -471,7 +512,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi IOUtils.copyLarge(macIn, new NullOutputStream()); // compare (in constant time): - final boolean macMatches = MessageDigest.isEqual(storedMac.array(), calculatedMac.doFinal()); + final boolean macMatches = MessageDigest.isEqual(contentMac, calculatedContentMac.doFinal()); if (!macMatches) { // This exception will be thrown AFTER we sent the decrypted content to the user. // This has two advantages: @@ -503,7 +544,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi countingIv.putInt(AES_BLOCK_LENGTH - Integer.BYTES, (int) firstRelevantBlock); // int-cast is possible, as max file size is 64GiB // fast forward stream: - encryptedFile.position(64l + beginOfFirstRelevantBlock); + encryptedFile.position(96l + beginOfFirstRelevantBlock); // generate cipher: final Cipher cipher = this.aesCtrCipher(primaryMasterKey, countingIv.array(), Cipher.DECRYPT_MODE); @@ -514,30 +555,30 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi return IOUtils.copyLarge(cipheredIn, plaintextFile, offsetInsideFirstRelevantBlock, length); } + /** + * header = {16 byte iv, 16 byte filesize, 32 byte headerMac, 32 byte contentMac} + */ @Override public Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException, EncryptFailedException { // truncate file encryptedFile.truncate(0); + // 96 byte header buffer (16 IV, 16 size, 32 headerMac, 32 contentMac): + final ByteBuffer headerBuf = ByteBuffer.allocate(96); + encryptedFile.write(headerBuf); + // use an IV, whose last 8 bytes store a long used in counter mode and write initial value to file. - final ByteBuffer countingIv = ByteBuffer.wrap(randomData(AES_BLOCK_LENGTH)); - countingIv.putInt(AES_BLOCK_LENGTH - Integer.BYTES, 0); - encryptedFile.write(countingIv); - - // init crypto stuff: - final Mac mac = this.hmacSha256(hMacMasterKey); - final Cipher cipher = this.aesCtrCipher(primaryMasterKey, countingIv.array(), Cipher.ENCRYPT_MODE); - - // init mac buffer and skip 32 bytes - final ByteBuffer macBuffer = ByteBuffer.allocate(mac.getMacLength()); - encryptedFile.write(macBuffer); + final ByteBuffer iv = ByteBuffer.wrap(randomData(AES_BLOCK_LENGTH)); + iv.putInt(AES_BLOCK_LENGTH - Integer.BYTES, 0); // encrypt and write "zero length" as a placeholder, which will be read by concurrent requests, as long as encryption didn't finish: - encryptedContentLength(encryptedFile, 0l); + // encryptedContentLength(encryptedFile, 0l); - // write content: + // content encryption: + final Cipher cipher = this.aesCtrCipher(primaryMasterKey, iv.array(), Cipher.ENCRYPT_MODE); + final Mac contentMac = this.hmacSha256(hMacMasterKey); final OutputStream out = new SeekableByteChannelOutputStream(encryptedFile); - final OutputStream macOut = new MacOutputStream(out, mac); + final OutputStream macOut = new MacOutputStream(out, contentMac); final OutputStream cipheredOut = new CipherOutputStream(macOut, cipher); final OutputStream blockSizeBufferedOut = new BufferedOutputStream(cipheredOut, AES_BLOCK_LENGTH); final InputStream lengthLimitingIn = new CounterAwareInputStream(plaintextFile); @@ -546,14 +587,15 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi plaintextSize = IOUtils.copyLarge(lengthLimitingIn, blockSizeBufferedOut); } catch (CounterAwareInputLimitReachedException ex) { encryptedFile.truncate(64l + CounterAwareInputStream.SIXTY_FOUR_GIGABYE); - encryptedContentLength(encryptedFile, CounterAwareInputStream.SIXTY_FOUR_GIGABYE); + // TODO + // encryptedContentLength(encryptedFile, CounterAwareInputStream.SIXTY_FOUR_GIGABYE); // no additional padding needed here, as 64GiB is a multiple of 128bit throw new CounterOverflowException("File size exceeds limit (64Gib). Aborting to prevent counter overflow."); } // ensure total byte count is a multiple of the block size, in CTR mode: - final int remainderToFillLastBlock = AES_BLOCK_LENGTH - (int) (plaintextSize % AES_BLOCK_LENGTH); - blockSizeBufferedOut.write(new byte[remainderToFillLastBlock]); + // final int remainderToFillLastBlock = AES_BLOCK_LENGTH - (int) (plaintextSize % AES_BLOCK_LENGTH); + // blockSizeBufferedOut.write(new byte[remainderToFillLastBlock]); // for filesizes of up to 16GiB: append a few blocks of fake data: if (plaintextSize < (long) (Integer.MAX_VALUE / 4) * AES_BLOCK_LENGTH) { @@ -566,15 +608,19 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi } blockSizeBufferedOut.flush(); - // write MAC of total ciphertext: - macBuffer.clear(); - macBuffer.put(mac.doFinal()); - macBuffer.flip(); - encryptedFile.position(16); // right behind the IV - encryptedFile.write(macBuffer); // 256 bit MAC - - // encrypt and write plaintextSize: - encryptedContentLength(encryptedFile, plaintextSize); + // create and write header: + headerBuf.clear(); + headerBuf.put(iv); + headerBuf.put(encryptContentLength(plaintextSize)); + headerBuf.flip(); + final Mac headerMac = this.hmacSha256(hMacMasterKey); + headerMac.update(headerBuf); + headerBuf.limit(96); + headerBuf.put(headerMac.doFinal()); + headerBuf.put(contentMac.doFinal()); + headerBuf.flip(); + encryptedFile.position(0); + encryptedFile.write(headerBuf); return plaintextSize; } diff --git a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java index 8c8a2bc7a..a32e4868c 100644 --- a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java +++ b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java @@ -82,7 +82,7 @@ public class Aes256CryptorTest { final Aes256Cryptor cryptor = new Aes256Cryptor(); // encrypt: - final ByteBuffer encryptedData = ByteBuffer.allocate(96); + final ByteBuffer encryptedData = ByteBuffer.allocate(256); final SeekableByteChannel encryptedOut = new ByteBufferBackedSeekableChannel(encryptedData); cryptor.encryptFile(plaintextIn, encryptedOut); IOUtils.closeQuietly(plaintextIn); @@ -114,7 +114,7 @@ public class Aes256CryptorTest { final Aes256Cryptor cryptor = new Aes256Cryptor(); // encrypt: - final ByteBuffer encryptedData = ByteBuffer.allocate(96); + final ByteBuffer encryptedData = ByteBuffer.allocate(256); final SeekableByteChannel encryptedOut = new ByteBufferBackedSeekableChannel(encryptedData); cryptor.encryptFile(plaintextIn, encryptedOut); IOUtils.closeQuietly(plaintextIn); @@ -146,7 +146,7 @@ public class Aes256CryptorTest { final Aes256Cryptor cryptor = new Aes256Cryptor(); // encrypt: - final ByteBuffer encryptedData = ByteBuffer.allocate(96); + final ByteBuffer encryptedData = ByteBuffer.allocate(256); final SeekableByteChannel encryptedOut = new ByteBufferBackedSeekableChannel(encryptedData); cryptor.encryptFile(plaintextIn, encryptedOut); IOUtils.closeQuietly(plaintextIn); @@ -185,7 +185,7 @@ public class Aes256CryptorTest { final Aes256Cryptor cryptor = new Aes256Cryptor(); // encrypt: - final ByteBuffer encryptedData = ByteBuffer.allocate((int) (64 + plaintextData.length * 1.2)); + final ByteBuffer encryptedData = ByteBuffer.allocate((int) (96 + plaintextData.length * 1.2)); final SeekableByteChannel encryptedOut = new ByteBufferBackedSeekableChannel(encryptedData); cryptor.encryptFile(plaintextIn, encryptedOut); IOUtils.closeQuietly(plaintextIn); diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java index 0a1b9b8b6..56eea4313 100644 --- a/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java +++ b/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java @@ -11,6 +11,7 @@ import javax.security.auth.DestroyFailedException; import org.cryptomator.crypto.exceptions.DecryptFailedException; import org.cryptomator.crypto.exceptions.EncryptFailedException; +import org.cryptomator.crypto.exceptions.MacAuthenticationFailedException; import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException; import org.cryptomator.crypto.exceptions.WrongPasswordException; @@ -48,7 +49,7 @@ public class AbstractCryptorDecorator implements Cryptor { } @Override - public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException { + public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException, MacAuthenticationFailedException { return cryptor.decryptedContentLength(encryptedFile); } diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java index 5a2cd25a0..1eecf0f21 100644 --- a/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java +++ b/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java @@ -19,6 +19,7 @@ import javax.security.auth.Destroyable; import org.cryptomator.crypto.exceptions.DecryptFailedException; import org.cryptomator.crypto.exceptions.EncryptFailedException; +import org.cryptomator.crypto.exceptions.MacAuthenticationFailedException; import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException; import org.cryptomator.crypto.exceptions.WrongPasswordException; @@ -36,20 +37,16 @@ public interface Cryptor extends Destroyable { * Reads the encrypted masterkey from the given input stream and decrypts it with the given password. * * @throws DecryptFailedException If the decryption failed for various reasons (including wrong password). - * @throws WrongPasswordException If the provided password was wrong. Note: Sometimes the algorithm itself fails due to a wrong - * password. In this case a DecryptFailedException will be thrown. - * @throws UnsupportedKeyLengthException If the masterkey has been encrypted with a higher key length than supported by the system. In - * this case Java JCE needs to be installed. + * @throws WrongPasswordException If the provided password was wrong. Note: Sometimes the algorithm itself fails due to a wrong password. In this case a DecryptFailedException will be thrown. + * @throws UnsupportedKeyLengthException If the masterkey has been encrypted with a higher key length than supported by the system. In this case Java JCE needs to be installed. */ void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException; /** - * Encrypts a given plaintext path representing a directory structure. See {@link #encryptFilename(String, CryptorMetadataSupport)} for - * contents inside directories. + * Encrypts a given plaintext path representing a directory structure. See {@link #encryptFilename(String, CryptorMetadataSupport)} for contents inside directories. * * @param cleartextPath A relative path (UTF-8 encoded), whose path components are separated by '/' - * @param nativePathSep Path separator like "/" used on local file system. Must not be null, even if cleartextPath is a sole file name - * without any path separators. + * @param nativePathSep Path separator like "/" used on local file system. Must not be null, even if cleartextPath is a sole file name without any path separators. * @return Encrypted path. */ String encryptDirectoryPath(String cleartextPath, String nativePathSep); @@ -58,8 +55,7 @@ public interface Cryptor extends Destroyable { * Encrypts the name of a file. See {@link #encryptDirectoryPath(String, char)} for parent dir. * * @param cleartextName A plaintext filename without any preceeding directory paths. - * @param ioSupport Support object allowing the Cryptor to read and write its own metadata to a storage space associated with this - * support object. + * @param ioSupport Support object allowing the Cryptor to read and write its own metadata to a storage space associated with this support object. * @return Encrypted filename. * @throws IOException If ioSupport throws an IOException */ @@ -69,8 +65,7 @@ public interface Cryptor extends Destroyable { * Decrypts the name of a file. * * @param ciphertextName A ciphertext filename without any preceeding directory paths. - * @param ioSupport Support object allowing the Cryptor to read and write its own metadata to a storage space associated with this - * support object. + * @param ioSupport Support object allowing the Cryptor to read and write its own metadata to a storage space associated with this support object. * @return Decrypted filename. * @throws DecryptFailedException If the decryption failed for various reasons (including wrong password). * @throws IOException If ioSupport throws an IOException @@ -80,8 +75,9 @@ public interface Cryptor extends Destroyable { /** * @param metadataSupport Support object allowing the Cryptor to read and write its own metadata to the location of the encrypted file. * @return Content length of the decrypted file or null if unknown. + * @throws MacAuthenticationFailedException If the MAC auth failed. */ - Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException; + Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException, MacAuthenticationFailedException; /** * @return true, if the stored MAC matches the calculated one. @@ -108,8 +104,7 @@ public interface Cryptor extends Destroyable { Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException, EncryptFailedException; /** - * @return A filter, that returns true for encrypted files, i.e. if the file is an actual user payload and not a supporting - * metadata file of the {@link Cryptor}. + * @return A filter, that returns true for encrypted files, i.e. if the file is an actual user payload and not a supporting metadata file of the {@link Cryptor}. */ Filter getPayloadFilesFilter();