Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#98 change vault init #99

Merged
merged 11 commits into from
Mar 16, 2021
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.cryptomator.cryptofs;

import java.nio.file.NoSuchFileException;

public class ContentRootMissingException extends NoSuchFileException {

public ContentRootMissingException(String msg) {
super(msg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public CryptoFileSystemImpl(CryptoFileSystemProvider provider, CryptoFileSystems
PathMatcherFactory pathMatcherFactory, DirectoryStreamFactory directoryStreamFactory, DirectoryIdProvider dirIdProvider,
AttributeProvider fileAttributeProvider, AttributeByNameProvider fileAttributeByNameProvider, AttributeViewProvider fileAttributeViewProvider,
OpenCryptoFiles openCryptoFiles, Symlinks symlinks, FinallyUtil finallyUtil, CiphertextDirectoryDeleter ciphertextDirDeleter, ReadonlyFlag readonlyFlag,
CryptoFileSystemProperties fileSystemProperties, RootDirectoryInitializer rootDirectoryInitializer) {
CryptoFileSystemProperties fileSystemProperties) {
this.provider = provider;
this.cryptoFileSystems = cryptoFileSystems;
this.pathToVault = pathToVault;
Expand All @@ -129,8 +129,6 @@ public CryptoFileSystemImpl(CryptoFileSystemProvider provider, CryptoFileSystems

this.rootPath = cryptoPathFactory.rootFor(this);
this.emptyPath = cryptoPathFactory.emptyFor(this);

rootDirectoryInitializer.initialize(rootPath);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.cryptomator.cryptofs.ch.AsyncDelegatingFileChannel;
import org.cryptomator.cryptofs.common.Constants;
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;

Expand Down Expand Up @@ -140,16 +141,18 @@ public static void initialize(Path pathToVault, CryptoFileSystemProperties prope
throw new NotDirectoryException(pathToVault.toString());
}
byte[] rawKey = new byte[0];
try (Masterkey key = properties.keyLoader(keyId.getScheme()).loadKey(keyId)) {
var config = VaultConfig.createNew().cipherCombo(properties.cipherCombo()).maxFilenameLength(properties.maxNameLength()).build();
try (Masterkey key = properties.keyLoader(keyId.getScheme()).loadKey(keyId);
Cryptor cryptor = config.getCipherCombo().getCryptorProvider(strongSecureRandom()).withKey(key)) {
rawKey = key.getEncoded();
// save vault config:
Path vaultConfigPath = pathToVault.resolve(properties.vaultConfigFilename());
var config = VaultConfig.createNew().cipherCombo(properties.cipherCombo()).maxFilenameLength(properties.maxNameLength()).build();
var token = config.toToken(keyId.toString(), rawKey);
Files.writeString(vaultConfigPath, token, StandardCharsets.US_ASCII, WRITE, CREATE_NEW);
// create "d" dir:
Path dataDirPath = pathToVault.resolve(Constants.DATA_DIR_NAME);
Files.createDirectories(dataDirPath);
// create "d" dir and root:
String dirHash = cryptor.fileNameCryptor().hashDirectoryId(Constants.ROOT_DIR_ID);
Path vaultCipherRootPath = pathToVault.resolve(Constants.DATA_DIR_NAME).resolve(dirHash.substring(0, 2)).resolve(dirHash.substring(2));
Files.createDirectories(vaultCipherRootPath);
} finally {
Arrays.fill(rawKey, (byte) 0x00);
}
Expand Down
42 changes: 31 additions & 11 deletions src/main/java/org/cryptomator/cryptofs/CryptoFileSystems.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,41 @@ public CryptoFileSystemImpl create(CryptoFileSystemProvider provider, Path pathT
rawKey = key.getEncoded();
var config = configLoader.verify(rawKey, Constants.VAULT_VERSION);
var adjustedProperties = adjustForCapabilities(pathToVault, properties);
return fileSystems.compute(normalizedPathToVault, (path, fs) -> {
if (fs == null) {
return create(provider, normalizedPathToVault, adjustedProperties, key, config);
} else {
throw new FileSystemAlreadyExistsException();
}
});
var cryptor = config.getCipherCombo().getCryptorProvider(csprng).withKey(key);
overheadhunter marked this conversation as resolved.
Show resolved Hide resolved
try {
checkVaultRootExistence(pathToVault, cryptor);
return fileSystems.compute(normalizedPathToVault, (path, fs) -> {
if (fs == null) {
return create(provider, normalizedPathToVault, adjustedProperties, cryptor, config);
} else {
throw new FileSystemAlreadyExistsException();
}
});
} catch (Exception e) { //on any exception, destroy the cryptor
cryptor.destroy();
throw e;
}
} finally {
Arrays.fill(rawKey, (byte) 0x00);
}
}

/**
* Checks if the vault has a content root folder. If not, an exception is raised.
* @param pathToVault Path to the vault root
* @param cryptor Cryptor object initialized with the correct masterkey
* @throws ContentRootMissingException If the existence of encrypted vault content root cannot be ensured
*/
private void checkVaultRootExistence(Path pathToVault, Cryptor cryptor) throws ContentRootMissingException {
String dirHash = cryptor.fileNameCryptor().hashDirectoryId(Constants.ROOT_DIR_ID);
Path vaultCipherRootPath = pathToVault.resolve(Constants.DATA_DIR_NAME).resolve(dirHash.substring(0, 2)).resolve(dirHash.substring(2));
overheadhunter marked this conversation as resolved.
Show resolved Hide resolved
if (!Files.exists(vaultCipherRootPath)) {
throw new ContentRootMissingException("The encrypted root directory of the vault " + pathToVault + " is missing.");
}
}

// synchronized access to non-threadsafe cryptoFileSystemComponentBuilder required
private synchronized CryptoFileSystemImpl create(CryptoFileSystemProvider provider, Path pathToVault, CryptoFileSystemProperties properties, Masterkey masterkey, VaultConfig config) {
Cryptor cryptor = config.getCipherCombo().getCryptorProvider(csprng).withKey(masterkey);
private synchronized CryptoFileSystemImpl create(CryptoFileSystemProvider provider, Path pathToVault, CryptoFileSystemProperties properties, Cryptor cryptor, VaultConfig config) {
return cryptoFileSystemComponentBuilder //
.cryptor(cryptor) //
.vaultConfig(config) //
Expand All @@ -83,9 +103,9 @@ private synchronized CryptoFileSystemImpl create(CryptoFileSystemProvider provid
* Attempts to read a vault config file
*
* @param pathToVault path to the vault's root
* @param properties properties used when attempting to construct a fs for this vault
* @param properties properties used when attempting to construct a fs for this vault
* @return The contents of the file decoded in ASCII
* @throws IOException If the file could not be read
* @throws IOException If the file could not be read
* @throws FileSystemNeedsMigrationException If the file doesn't exists, but a legacy masterkey file was found instead
*/
private String readVaultConfigFile(Path pathToVault, CryptoFileSystemProperties properties) throws IOException, FileSystemNeedsMigrationException {
Expand Down
26 changes: 0 additions & 26 deletions src/main/java/org/cryptomator/cryptofs/FilesWrapper.java

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ public class CryptoFileSystemImplTest {
private final PathMatcherFactory pathMatcherFactory = mock(PathMatcherFactory.class);
private final CryptoPathFactory cryptoPathFactory = mock(CryptoPathFactory.class);
private final CryptoFileSystemStats stats = mock(CryptoFileSystemStats.class);
private final RootDirectoryInitializer rootDirectoryInitializer = mock(RootDirectoryInitializer.class);
private final DirectoryStreamFactory directoryStreamFactory = mock(DirectoryStreamFactory.class);
private final FinallyUtil finallyUtil = mock(FinallyUtil.class);
private final CiphertextDirectoryDeleter ciphertextDirDeleter = mock(CiphertextDirectoryDeleter.class);
Expand Down Expand Up @@ -124,7 +123,7 @@ public void setup() {
pathMatcherFactory, directoryStreamFactory, dirIdProvider,
fileAttributeProvider, fileAttributeByNameProvider, fileAttributeViewProvider,
openCryptoFiles, symlinks, finallyUtil, ciphertextDirDeleter, readonlyFlag,
fileSystemProperties, rootDirectoryInitializer);
fileSystemProperties);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.nio.file.spi.FileSystemProvider;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.stream.Stream;
Expand Down Expand Up @@ -187,6 +188,14 @@ public void testInitialize() throws IOException, MasterkeyLoadingFailedException

Assertions.assertTrue(Files.isDirectory(dataDir));
Assertions.assertTrue(Files.isRegularFile(vaultConfigFile));

Optional<Path> preRootDir = Files.list(dataDir).findFirst();
Assertions.assertTrue(preRootDir.isPresent());
Assertions.assertTrue(Files.isDirectory(preRootDir.get()));

Optional<Path> rootDir = Files.list(preRootDir.get()).findFirst();
Assertions.assertTrue(rootDir.isPresent());
Assertions.assertTrue(Files.isDirectory(rootDir.get()));
}

@Test
Expand Down
17 changes: 17 additions & 0 deletions src/test/java/org/cryptomator/cryptofs/CryptoFileSystemsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.CryptorProvider;
import org.cryptomator.cryptolib.api.FileNameCryptor;
import org.cryptomator.cryptolib.api.Masterkey;
import org.cryptomator.cryptolib.api.MasterkeyLoader;
import org.cryptomator.cryptolib.api.MasterkeyLoadingFailedException;
Expand Down Expand Up @@ -35,6 +36,9 @@ public class CryptoFileSystemsTest {
private final Path pathToVault = mock(Path.class, "vaultPath");
private final Path normalizedPathToVault = mock(Path.class, "normalizedVaultPath");
private final Path configFilePath = mock(Path.class, "normalizedVaultPath/vault.cryptomator");
private final Path dataDirPath = mock(Path.class, "normalizedVaultPath/d");
private final Path preContenRootPath = mock(Path.class, "normalizedVaultPath/d/AB");
private final Path contenRootPath = mock(Path.class, "normalizedVaultPath/d/AB/CDEFGHIJKLMNOP");
private final FileSystemCapabilityChecker capabilityChecker = mock(FileSystemCapabilityChecker.class);
private final CryptoFileSystemProvider provider = mock(CryptoFileSystemProvider.class);
private final CryptoFileSystemProperties properties = mock(CryptoFileSystemProperties.class);
Expand All @@ -49,6 +53,7 @@ public class CryptoFileSystemsTest {
private final SecureRandom csprng = Mockito.mock(SecureRandom.class);
private final CryptorProvider cryptorProvider = mock(CryptorProvider.class);
private final Cryptor cryptor = mock(Cryptor.class);
private final FileNameCryptor fileNameCryptor = mock(FileNameCryptor.class);
private final CryptoFileSystemComponent.Builder cryptoFileSystemComponentBuilder = mock(CryptoFileSystemComponent.Builder.class);


Expand Down Expand Up @@ -76,6 +81,12 @@ public void setup() throws IOException, MasterkeyLoadingFailedException {
when(vaultConfig.getCipherCombo()).thenReturn(cipherCombo);
when(cipherCombo.getCryptorProvider(csprng)).thenReturn(cryptorProvider);
when(cryptorProvider.withKey(masterkey)).thenReturn(cryptor);
when(cryptor.fileNameCryptor()).thenReturn(fileNameCryptor);
when(fileNameCryptor.hashDirectoryId("")).thenReturn("ABCDEFGHIJKLMNOP");
when(pathToVault.resolve(Constants.DATA_DIR_NAME)).thenReturn(dataDirPath);
when(dataDirPath.resolve("AB")).thenReturn(preContenRootPath);
when(preContenRootPath.resolve("CDEFGHIJKLMNOP")).thenReturn(contenRootPath);
filesClass.when(() -> Files.exists(contenRootPath)).thenReturn(true);
when(cryptoFileSystemComponentBuilder.cryptor(any())).thenReturn(cryptoFileSystemComponentBuilder);
when(cryptoFileSystemComponentBuilder.vaultConfig(any())).thenReturn(cryptoFileSystemComponentBuilder);
when(cryptoFileSystemComponentBuilder.pathToVault(any())).thenReturn(cryptoFileSystemComponentBuilder);
Expand Down Expand Up @@ -130,6 +141,12 @@ public void testCreateDoesNotThrowFileSystemAlreadyExistsExceptionIfFileSystemIs
Assertions.assertTrue(inTest.contains(fileSystem2));
}

@Test
public void testCreateThrowsIOExceptionIfContentRootExistenceCheckFails() {
filesClass.when(() -> Files.exists(contenRootPath)).thenReturn(false);
Assertions.assertThrows(IOException.class, () -> inTest.create(provider, pathToVault, properties));
}

@Test
public void testGetReturnsFileSystemForPathIfItExists() throws IOException, MasterkeyLoadingFailedException {
CryptoFileSystemImpl fileSystem = inTest.create(provider, pathToVault, properties);
Expand Down

This file was deleted.