mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 02:31:27 +00:00
Proper error handling for outdated vault formats
This commit is contained in:
@@ -50,6 +50,7 @@ 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.UnsupportedVaultException;
|
||||
import org.cryptomator.crypto.exceptions.WrongPasswordException;
|
||||
import org.cryptomator.crypto.io.SeekableByteChannelInputStream;
|
||||
import org.cryptomator.crypto.io.SeekableByteChannelOutputStream;
|
||||
@@ -133,6 +134,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
|
||||
// save encrypted masterkey:
|
||||
final KeyFile keyfile = new KeyFile();
|
||||
keyfile.setVersion(KeyFile.CURRENT_VERSION);
|
||||
keyfile.setScryptSalt(kekSalt);
|
||||
keyfile.setScryptCostParam(SCRYPT_COST_PARAM);
|
||||
keyfile.setScryptBlockSize(SCRYPT_BLOCK_SIZE);
|
||||
@@ -151,13 +153,19 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
* @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 UnsupportedVaultException If the masterkey file is too old or too modern.
|
||||
*/
|
||||
@Override
|
||||
public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException {
|
||||
public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException, UnsupportedVaultException {
|
||||
try {
|
||||
// load encrypted masterkey:
|
||||
final KeyFile keyfile = objectMapper.readValue(in, KeyFile.class);
|
||||
|
||||
// check version
|
||||
if (keyfile.getVersion() != KeyFile.CURRENT_VERSION) {
|
||||
throw new UnsupportedVaultException(keyfile.getVersion(), KeyFile.CURRENT_VERSION);
|
||||
}
|
||||
|
||||
// check, whether the key length is supported:
|
||||
final int maxKeyLen = Cipher.getMaxAllowedKeyLength(AES_KEY_ALGORITHM);
|
||||
if (keyfile.getKeyLength() > maxKeyLen) {
|
||||
@@ -225,17 +233,16 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
}
|
||||
}
|
||||
|
||||
private Cipher aesEcbCipher(SecretKey key, int cipherMode) {
|
||||
private Cipher aesCbcCipher(SecretKey key, byte[] iv, int cipherMode) {
|
||||
try {
|
||||
final Cipher cipher = Cipher.getInstance(AES_ECB_CIPHER);
|
||||
cipher.init(cipherMode, key);
|
||||
final Cipher cipher = Cipher.getInstance(AES_CBC_CIPHER);
|
||||
cipher.init(cipherMode, key, new IvParameterSpec(iv));
|
||||
return cipher;
|
||||
} catch (InvalidKeyException ex) {
|
||||
throw new IllegalArgumentException("Invalid key.", ex);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {
|
||||
throw new AssertionError("Every implementation of the Java platform is required to support AES/ECB/PKCS5Padding.", ex);
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException ex) {
|
||||
throw new AssertionError("Every implementation of the Java platform is required to support AES/CBC/PKCS5Padding, which accepts an IV", ex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Mac hmacSha256(SecretKey key) {
|
||||
@@ -291,7 +298,7 @@ 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.<br/>
|
||||
* 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.<br/>
|
||||
* Encryption will blow up the filename length due to aes block sizes, IVs and base32 encoding. The result may be too long for some old file systems.<br/>
|
||||
* This means that we need a workaround for filenames longer than the limit defined in {@link FileNamingConventions#ENCRYPTED_FILENAME_LENGTH_LIMIT}.<br/>
|
||||
* <br/>
|
||||
* 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
|
||||
@@ -364,15 +371,20 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
return null;
|
||||
}
|
||||
|
||||
// read iv:
|
||||
final byte[] iv = new byte[AES_BLOCK_LENGTH];
|
||||
headerBuf.position(0);
|
||||
headerBuf.get(iv);
|
||||
|
||||
// read content length:
|
||||
headerBuf.position(16);
|
||||
final byte[] encryptedContentLengthBytes = new byte[AES_BLOCK_LENGTH];
|
||||
headerBuf.position(16);
|
||||
headerBuf.get(encryptedContentLengthBytes);
|
||||
final Long fileSize = decryptContentLength(encryptedContentLengthBytes);
|
||||
final Long fileSize = decryptContentLength(encryptedContentLengthBytes, iv);
|
||||
|
||||
// read stored header mac:
|
||||
headerBuf.position(32);
|
||||
final byte[] storedHeaderMac = new byte[32];
|
||||
headerBuf.position(32);
|
||||
headerBuf.get(storedHeaderMac);
|
||||
|
||||
// calculate mac over first 32 bytes of header:
|
||||
@@ -389,9 +401,9 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
private long decryptContentLength(byte[] encryptedContentLengthBytes) {
|
||||
private long decryptContentLength(byte[] encryptedContentLengthBytes, byte[] iv) {
|
||||
try {
|
||||
final Cipher sizeCipher = aesEcbCipher(primaryMasterKey, Cipher.DECRYPT_MODE);
|
||||
final Cipher sizeCipher = aesCbcCipher(primaryMasterKey, iv, Cipher.DECRYPT_MODE);
|
||||
final byte[] decryptedFileSize = sizeCipher.doFinal(encryptedContentLengthBytes);
|
||||
final ByteBuffer fileSizeBuffer = ByteBuffer.wrap(decryptedFileSize);
|
||||
return fileSizeBuffer.getLong();
|
||||
@@ -400,11 +412,11 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] encryptContentLength(long contentLength) {
|
||||
private byte[] encryptContentLength(long contentLength, byte[] iv) {
|
||||
try {
|
||||
final ByteBuffer fileSizeBuffer = ByteBuffer.allocate(Long.BYTES);
|
||||
fileSizeBuffer.putLong(contentLength);
|
||||
final Cipher sizeCipher = aesEcbCipher(primaryMasterKey, Cipher.ENCRYPT_MODE);
|
||||
final Cipher sizeCipher = aesCbcCipher(primaryMasterKey, iv, Cipher.ENCRYPT_MODE);
|
||||
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);
|
||||
@@ -414,7 +426,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
@Override
|
||||
public boolean isAuthentic(SeekableByteChannel encryptedFile) throws IOException {
|
||||
// read header:
|
||||
encryptedFile.position(0);
|
||||
encryptedFile.position(0l);
|
||||
final ByteBuffer headerBuf = ByteBuffer.allocate(96);
|
||||
final int headerBytesRead = encryptedFile.read(headerBuf);
|
||||
if (headerBytesRead != headerBuf.capacity()) {
|
||||
@@ -422,23 +434,23 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
}
|
||||
|
||||
// read header mac:
|
||||
headerBuf.position(32);
|
||||
final byte[] storedHeaderMac = new byte[32];
|
||||
headerBuf.position(32);
|
||||
headerBuf.get(storedHeaderMac);
|
||||
|
||||
// read content mac:
|
||||
headerBuf.position(64);
|
||||
final byte[] storedContentMac = new byte[32];
|
||||
headerBuf.position(64);
|
||||
headerBuf.get(storedContentMac);
|
||||
|
||||
// calculate mac over first 32 bytes of header:
|
||||
final Mac headerMac = this.hmacSha256(hMacMasterKey);
|
||||
headerBuf.rewind();
|
||||
headerBuf.position(0);
|
||||
headerBuf.limit(32);
|
||||
headerMac.update(headerBuf);
|
||||
|
||||
// calculate mac over content:
|
||||
encryptedFile.position(96);
|
||||
encryptedFile.position(96l);
|
||||
final Mac contentMac = this.hmacSha256(hMacMasterKey);
|
||||
final InputStream in = new SeekableByteChannelInputStream(encryptedFile);
|
||||
final InputStream macIn = new MacInputStream(in, contentMac);
|
||||
@@ -453,29 +465,32 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
@Override
|
||||
public Long decryptFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile) throws IOException, DecryptFailedException {
|
||||
// read header:
|
||||
encryptedFile.position(0);
|
||||
encryptedFile.position(0l);
|
||||
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();
|
||||
|
||||
// read iv:
|
||||
final byte[] iv = new byte[AES_BLOCK_LENGTH];
|
||||
headerBuf.position(0);
|
||||
headerBuf.get(iv);
|
||||
|
||||
// read content length:
|
||||
final byte[] encryptedContentLengthBytes = new byte[AES_BLOCK_LENGTH];
|
||||
headerBuf.position(16);
|
||||
headerBuf.get(encryptedContentLengthBytes);
|
||||
final Long fileSize = decryptContentLength(encryptedContentLengthBytes);
|
||||
final Long fileSize = decryptContentLength(encryptedContentLengthBytes, iv);
|
||||
|
||||
// read header mac:
|
||||
final byte[] headerMac = new byte[32];
|
||||
headerBuf.position(32);
|
||||
headerBuf.get(headerMac);
|
||||
|
||||
// read content mac:
|
||||
final byte[] contentMac = new byte[32];
|
||||
headerBuf.position(64);
|
||||
headerBuf.get(contentMac);
|
||||
|
||||
// decrypt content
|
||||
@@ -507,7 +522,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
@Override
|
||||
public Long decryptRange(SeekableByteChannel encryptedFile, OutputStream plaintextFile, long pos, long length) throws IOException, DecryptFailedException {
|
||||
// read iv:
|
||||
encryptedFile.position(0);
|
||||
encryptedFile.position(0l);
|
||||
final ByteBuffer countingIv = ByteBuffer.allocate(AES_BLOCK_LENGTH);
|
||||
final int numIvBytesRead = encryptedFile.read(countingIv);
|
||||
|
||||
@@ -540,23 +555,24 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
@Override
|
||||
public Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException, EncryptFailedException {
|
||||
// truncate file
|
||||
encryptedFile.truncate(0);
|
||||
encryptedFile.truncate(0l);
|
||||
|
||||
// use an IV, whose last 8 bytes store a long used in counter mode and write initial value to file.
|
||||
final ByteBuffer iv = ByteBuffer.wrap(randomData(AES_BLOCK_LENGTH));
|
||||
iv.putInt(AES_BLOCK_LENGTH - Integer.BYTES, 0);
|
||||
final ByteBuffer ivBuf = ByteBuffer.wrap(randomData(AES_BLOCK_LENGTH));
|
||||
ivBuf.putInt(AES_BLOCK_LENGTH - Integer.BYTES, 0);
|
||||
final byte[] iv = ivBuf.array();
|
||||
|
||||
// 96 byte header buffer (16 IV, 16 size, 32 headerMac, 32 contentMac)
|
||||
// prefilled with "zero" content length for impatient processes, which want to know the size, before file has been completely written:
|
||||
final ByteBuffer headerBuf = ByteBuffer.allocate(96);
|
||||
headerBuf.position(16);
|
||||
headerBuf.put(encryptContentLength(0l));
|
||||
headerBuf.put(encryptContentLength(0l, iv));
|
||||
headerBuf.flip();
|
||||
headerBuf.limit(96);
|
||||
encryptedFile.write(headerBuf);
|
||||
|
||||
// content encryption:
|
||||
final Cipher cipher = this.aesCtrCipher(primaryMasterKey, iv.array(), Cipher.ENCRYPT_MODE);
|
||||
final Cipher cipher = this.aesCtrCipher(primaryMasterKey, iv, Cipher.ENCRYPT_MODE);
|
||||
final Mac contentMac = this.hmacSha256(hMacMasterKey);
|
||||
final OutputStream out = new SeekableByteChannelOutputStream(encryptedFile);
|
||||
final OutputStream macOut = new MacOutputStream(out, contentMac);
|
||||
@@ -586,7 +602,7 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
// create and write header:
|
||||
headerBuf.clear();
|
||||
headerBuf.put(iv);
|
||||
headerBuf.put(encryptContentLength(plaintextSize));
|
||||
headerBuf.put(encryptContentLength(plaintextSize, iv));
|
||||
headerBuf.flip();
|
||||
final Mac headerMac = this.hmacSha256(hMacMasterKey);
|
||||
headerMac.update(headerBuf);
|
||||
|
||||
@@ -26,14 +26,14 @@ interface AesCryptographicConfiguration {
|
||||
int SCRYPT_BLOCK_SIZE = 8;
|
||||
|
||||
/**
|
||||
* Number of bytes of the master key. Should be the maximum possible AES key length to provide best security.
|
||||
* Preferred number of bytes of the master key.
|
||||
*/
|
||||
int PREF_MASTER_KEY_LENGTH_IN_BITS = 256;
|
||||
|
||||
/**
|
||||
* Number of bytes used as seed for the PRNG.
|
||||
*/
|
||||
int PRNG_SEED_LENGTH = 32;
|
||||
int PRNG_SEED_LENGTH = 16;
|
||||
|
||||
/**
|
||||
* Algorithm used for random number generation.
|
||||
@@ -60,30 +60,22 @@ interface AesCryptographicConfiguration {
|
||||
String AES_KEYWRAP_CIPHER = "AESWrap";
|
||||
|
||||
/**
|
||||
* Cipher specs for file name and file content encryption. Using CTR-mode for random access.<br/>
|
||||
* <strong>Important</strong>: As JCE doesn't support a padding, input must be a multiple of the block size.
|
||||
* Cipher specs for file content encryption. Using CTR-mode for random access.<br/>
|
||||
*
|
||||
* @see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher
|
||||
*/
|
||||
String AES_CTR_CIPHER = "AES/CTR/NoPadding";
|
||||
|
||||
/**
|
||||
* Cipher specs for single block encryption (like file size).
|
||||
* Cipher specs for file header encryption (fixed-length block cipher).<br/>
|
||||
*
|
||||
* @see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#impl
|
||||
*/
|
||||
String AES_ECB_CIPHER = "AES/ECB/PKCS5Padding";
|
||||
String AES_CBC_CIPHER = "AES/CBC/PKCS5Padding";
|
||||
|
||||
/**
|
||||
* AES block size is 128 bit or 16 bytes.
|
||||
*/
|
||||
int AES_BLOCK_LENGTH = 16;
|
||||
|
||||
/**
|
||||
* Number of non-zero bytes in the IV used for file name encryption. Less means shorter encrypted filenames, more means higher entropy.
|
||||
* Maximum length is {@value #AES_BLOCK_LENGTH}. Even the shortest base32 (see {@link FileNamingConventions#ENCRYPTED_FILENAME_CODEC})
|
||||
* encoded byte array will need 8 chars. The maximum number of bytes that fit in 8 base32 chars is 5. Thus 5 is the ideal length.
|
||||
*/
|
||||
int FILE_NAME_IV_LENGTH = 5;
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ interface FileNamingConventions {
|
||||
BaseNCodec ENCRYPTED_FILENAME_CODEC = new Base32();
|
||||
|
||||
/**
|
||||
* Maximum length possible on file systems with a filename limit of 255 chars.<br/>
|
||||
* Maximum length possible on file systems with a filename or even path length limit of 255 chars.<br/>
|
||||
* Also we would need a few chars for our file extension, so lets use {@value #ENCRYPTED_FILENAME_LENGTH_LIMIT}.
|
||||
*/
|
||||
int ENCRYPTED_FILENAME_LENGTH_LIMIT = 128;
|
||||
|
||||
@@ -4,10 +4,13 @@ import java.io.Serializable;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
|
||||
|
||||
@JsonPropertyOrder(value = {"scryptSalt", "scryptCostParam", "scryptBlockSize", "keyLength", "primaryMasterKey", "hMacMasterKey"})
|
||||
@JsonPropertyOrder(value = {"version", "scryptSalt", "scryptCostParam", "scryptBlockSize", "keyLength", "primaryMasterKey", "hMacMasterKey"})
|
||||
public class KeyFile implements Serializable {
|
||||
|
||||
static final Integer CURRENT_VERSION = 1;
|
||||
private static final long serialVersionUID = 8578363158959619885L;
|
||||
|
||||
private Integer version;
|
||||
private byte[] scryptSalt;
|
||||
private int scryptCostParam;
|
||||
private int scryptBlockSize;
|
||||
@@ -15,6 +18,14 @@ public class KeyFile implements Serializable {
|
||||
private byte[] primaryMasterKey;
|
||||
private byte[] hMacMasterKey;
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Integer version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public byte[] getScryptSalt() {
|
||||
return scryptSalt;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.cryptomator.crypto.CryptorMetadataSupport;
|
||||
import org.cryptomator.crypto.exceptions.DecryptFailedException;
|
||||
import org.cryptomator.crypto.exceptions.EncryptFailedException;
|
||||
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
|
||||
import org.cryptomator.crypto.exceptions.UnsupportedVaultException;
|
||||
import org.cryptomator.crypto.exceptions.WrongPasswordException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
@@ -32,7 +33,7 @@ import org.junit.Test;
|
||||
public class Aes256CryptorTest {
|
||||
|
||||
@Test
|
||||
public void testCorrectPassword() throws IOException, WrongPasswordException, DecryptFailedException, UnsupportedKeyLengthException, DestroyFailedException {
|
||||
public void testCorrectPassword() throws IOException, WrongPasswordException, DecryptFailedException, UnsupportedKeyLengthException, DestroyFailedException, UnsupportedVaultException {
|
||||
final String pw = "asd";
|
||||
final Aes256Cryptor cryptor = new Aes256Cryptor();
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
@@ -48,7 +49,7 @@ public class Aes256CryptorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongPassword() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, DestroyFailedException {
|
||||
public void testWrongPassword() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, DestroyFailedException, UnsupportedVaultException {
|
||||
final String pw = "asd";
|
||||
final Aes256Cryptor cryptor = new Aes256Cryptor();
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
@@ -13,6 +13,7 @@ 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.UnsupportedVaultException;
|
||||
import org.cryptomator.crypto.exceptions.WrongPasswordException;
|
||||
|
||||
public class AbstractCryptorDecorator implements Cryptor {
|
||||
@@ -29,7 +30,7 @@ public class AbstractCryptorDecorator implements Cryptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException {
|
||||
public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException, UnsupportedVaultException {
|
||||
cryptor.decryptMasterKey(in, password);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ 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.UnsupportedVaultException;
|
||||
import org.cryptomator.crypto.exceptions.WrongPasswordException;
|
||||
|
||||
/**
|
||||
@@ -39,8 +40,9 @@ public interface Cryptor extends Destroyable {
|
||||
* @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 UnsupportedVaultException If the masterkey file is too old or too modern.
|
||||
*/
|
||||
void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException;
|
||||
void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException, UnsupportedVaultException;
|
||||
|
||||
/**
|
||||
* Encrypts a given plaintext path representing a directory structure. See {@link #encryptFilename(String, CryptorMetadataSupport)} for contents inside directories.
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package org.cryptomator.crypto.exceptions;
|
||||
|
||||
public class UnsupportedVaultException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = -5147549533387945622L;
|
||||
|
||||
private final Integer detectedVersion;
|
||||
private final Integer supportedVersion;
|
||||
|
||||
public UnsupportedVaultException(Integer detectedVersion, Integer supportedVersion) {
|
||||
super("Tried to open vault of version " + detectedVersion + ", but can only handle version " + supportedVersion);
|
||||
this.detectedVersion = detectedVersion;
|
||||
this.supportedVersion = supportedVersion;
|
||||
}
|
||||
|
||||
public Integer getDetectedVersion() {
|
||||
return detectedVersion;
|
||||
}
|
||||
|
||||
public Integer getSupportedVersion() {
|
||||
return supportedVersion;
|
||||
}
|
||||
|
||||
public boolean isVaultOlderThanSoftware() {
|
||||
return detectedVersion == null || detectedVersion < supportedVersion;
|
||||
}
|
||||
|
||||
public boolean isSoftwareOlderThanVault() {
|
||||
return detectedVersion > supportedVersion;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import javafx.scene.control.Label;
|
||||
|
||||
import org.cryptomator.crypto.exceptions.DecryptFailedException;
|
||||
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
|
||||
import org.cryptomator.crypto.exceptions.UnsupportedVaultException;
|
||||
import org.cryptomator.crypto.exceptions.WrongPasswordException;
|
||||
import org.cryptomator.ui.controls.SecPasswordField;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
@@ -108,6 +109,14 @@ public class ChangePasswordController implements Initializable {
|
||||
newPasswordField.swipe();
|
||||
retypePasswordField.swipe();
|
||||
return;
|
||||
} catch (UnsupportedVaultException e) {
|
||||
if (e.isVaultOlderThanSoftware()) {
|
||||
messageLabel.setText(rb.getString("changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware"));
|
||||
} else if (e.isSoftwareOlderThanVault()) {
|
||||
messageLabel.setText(rb.getString("changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault"));
|
||||
}
|
||||
newPasswordField.swipe();
|
||||
retypePasswordField.swipe();
|
||||
} finally {
|
||||
oldPasswordField.swipe();
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import javax.security.auth.DestroyFailedException;
|
||||
import org.apache.commons.lang3.CharUtils;
|
||||
import org.cryptomator.crypto.exceptions.DecryptFailedException;
|
||||
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
|
||||
import org.cryptomator.crypto.exceptions.UnsupportedVaultException;
|
||||
import org.cryptomator.crypto.exceptions.WrongPasswordException;
|
||||
import org.cryptomator.ui.controls.SecPasswordField;
|
||||
import org.cryptomator.ui.model.Vault;
|
||||
@@ -131,6 +132,14 @@ public class UnlockController implements Initializable {
|
||||
progressIndicator.setVisible(false);
|
||||
messageLabel.setText(rb.getString("unlock.errorMessage.unsupportedKeyLengthInstallJCE"));
|
||||
LOG.warn("Unsupported Key-Length. Please install Oracle Java Cryptography Extension (JCE).", ex);
|
||||
} catch (UnsupportedVaultException e) {
|
||||
setControlsDisabled(false);
|
||||
progressIndicator.setVisible(false);
|
||||
if (e.isVaultOlderThanSoftware()) {
|
||||
messageLabel.setText(rb.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware"));
|
||||
} else if (e.isSoftwareOlderThanVault()) {
|
||||
messageLabel.setText(rb.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault"));
|
||||
}
|
||||
} catch (DestroyFailedException e) {
|
||||
setControlsDisabled(false);
|
||||
progressIndicator.setVisible(false);
|
||||
|
||||
@@ -32,6 +32,8 @@ unlock.button.unlock=Unlock vault
|
||||
unlock.errorMessage.wrongPassword=Wrong password.
|
||||
unlock.errorMessage.decryptionFailed=Decryption failed.
|
||||
unlock.errorMessage.unsupportedKeyLengthInstallJCE=Decryption failed. Please install Oracle JCE Unlimited Strength Policy.
|
||||
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware=Unsupported vault. This vault has been created with an older version of Cryptomator.
|
||||
unlock.errorMessage.unsupportedVersion.softwareOlderThanVault=Unsupported vault. This vault has been created with a newer version of Cryptomator.
|
||||
unlock.messageLabel.startServerFailed=Starting WebDAV server failed.
|
||||
|
||||
# change_password.fxml
|
||||
@@ -42,6 +44,8 @@ changePassword.button.unlock=Change password
|
||||
changePassword.errorMessage.wrongPassword=Wrong password.
|
||||
changePassword.errorMessage.decryptionFailed=Decryption failed.
|
||||
changePassword.errorMessage.unsupportedKeyLengthInstallJCE=Decryption failed. Please install Oracle JCE Unlimited Strength Policy.
|
||||
changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware=Unsupported vault. This vault has been created with an older version of Cryptomator.
|
||||
changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault=Unsupported vault. This vault has been created with a newer version of Cryptomator.
|
||||
changePassword.infoMessage.success=Password changed.
|
||||
|
||||
# unlocked.fxml
|
||||
|
||||
Reference in New Issue
Block a user