Skip to content

Commit

Permalink
Merge branch 'release/1.3.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
overheadhunter committed Jun 19, 2017
2 parents 7f9583a + 3b49095 commit 62949e1
Show file tree
Hide file tree
Showing 20 changed files with 741 additions and 89 deletions.
12 changes: 6 additions & 6 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>cryptofs</artifactId>
<version>1.3.2</version>
<version>1.4.0</version>
<name>Cryptomator Crypto Filesystem</name>
<description>This library provides the Java filesystem provider used by Cryptomator.</description>
<url>https://github.com/cryptomator/cryptofs</url>
Expand All @@ -15,10 +15,10 @@

<properties>
<java.version>1.8</java.version>
<cryptolib.version>1.1.2</cryptolib.version>
<cryptolib.version>1.1.5</cryptolib.version>
<dagger.version>2.11</dagger.version>
<guava.version>22.0</guava.version>
<commons.lang.version>3.5</commons.lang.version>
<commons.lang.version>3.6</commons.lang.version>
<slf4j.version>1.7.25</slf4j.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
Expand All @@ -34,14 +34,14 @@
<developers>
<developer>
<name>Sebastian Stenzel</name>
<email>sebastian.stenzel@gmail.com</email>
<email>sebastian.stenzel@skymatic.de</email>
<timezone>+1</timezone>
<organization>cryptomator.org</organization>
<organizationUrl>http://cryptomator.org</organizationUrl>
</developer>
<developer>
<name>Markus Kreusch</name>
<email>mail@markuskreusch.de</email>
<email>markus.kreusch@skymatic.de</email>
<timezone>+1</timezone>
<organization>cryptomator.org</organization>
<organizationUrl>http://cryptomator.org</organizationUrl>
Expand Down Expand Up @@ -108,7 +108,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.8.9</version>
<version>2.8.47</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/cryptomator/cryptofs/ConflictResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,11 @@ private boolean hasSameDirFileContent(Path conflictingPath, Path canonicalPath)
ReadableByteChannel in2 = Files.newByteChannel(canonicalPath, StandardOpenOption.READ)) {
ByteBuffer buf1 = ByteBuffer.allocate(MAX_DIR_FILE_SIZE);
ByteBuffer buf2 = ByteBuffer.allocate(MAX_DIR_FILE_SIZE);
in1.read(buf1);
in2.read(buf2);
int read1 = in1.read(buf1);
int read2 = in2.read(buf2);
buf1.flip();
buf2.flip();
return buf1.compareTo(buf2) == 0;
return read1 == read2 && buf1.compareTo(buf2) == 0;
}
}

Expand Down
21 changes: 11 additions & 10 deletions src/main/java/org/cryptomator/cryptofs/Constants.java
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* Copyright (c) 2016, 2017 Sebastian Stenzel and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE.txt.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
*******************************************************************************/
package org.cryptomator.cryptofs;

final class Constants {
public final class Constants {

public static final int VAULT_VERSION = 6;
public static final String MASTERKEY_BACKUP_SUFFIX = ".bkup";
public static final String DATA_DIR_NAME = "d";
public static final String METADATA_DIR_NAME = "m";
public static final String DIR_PREFIX = "0";
public static final int NAME_SHORTENING_THRESHOLD = 129;
public static final int VAULT_VERSION = 5;
public static final String ROOT_DIR_ID = "";

public static final String SEPARATOR = "/";
static final String DATA_DIR_NAME = "d";
static final String METADATA_DIR_NAME = "m";
static final String DIR_PREFIX = "0";
static final int NAME_SHORTENING_THRESHOLD = 129;
static final String ROOT_DIR_ID = "";

static final String SEPARATOR = "/";

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.AbstractMap;
import java.util.Collection;
import java.util.EnumSet;
Expand Down Expand Up @@ -65,14 +67,22 @@ public class CryptoFileSystemProperties extends AbstractMap<String, Object> {
*/
public static final String PROPERTY_FILESYSTEM_FLAGS = "flags";

static final Set<FileSystemFlags> DEFAULT_FILESYSTEM_FLAGS = unmodifiableSet(EnumSet.of(FileSystemFlags.INIT_IMPLICITLY));
static final Set<FileSystemFlags> DEFAULT_FILESYSTEM_FLAGS = unmodifiableSet(EnumSet.of(FileSystemFlags.MIGRATE_IMPLICITLY, FileSystemFlags.INIT_IMPLICITLY));

public enum FileSystemFlags {
/**
* If present, the vault is opened in read-only mode.
*/
READONLY,

/**
* If present, the vault gets automatically migrated during file system creation, which might become significantly slower.
* If absent, a {@link FileSystemNeedsMigrationException} will get thrown during the attempt to open a vault that needs migration.
*
* @since 1.4.0
*/
MIGRATE_IMPLICITLY,

/**
* If present, the vault structure will implicitly get initialized upon filesystem creation.
*
Expand Down Expand Up @@ -109,6 +119,10 @@ boolean readonly() {
return flags().contains(FileSystemFlags.READONLY);
}

boolean migrateImplicitly() {
return flags().contains(FileSystemFlags.MIGRATE_IMPLICITLY);
}

boolean initializeImplicitly() {
return flags().contains(FileSystemFlags.INIT_IMPLICITLY);
}
Expand Down Expand Up @@ -150,6 +164,18 @@ public static Builder cryptoFileSystemProperties() {
return new Builder();
}

/**
* Starts construction of {@code CryptoFileSystemProperties}.
* Convenience function for <code>cryptoFileSystemProperties().withPassphrase(passphrase)</code>.
*
* @param passphrase the passphrase to use
* @return a {@link Builder} which can be used to construct {@code CryptoFileSystemProperties}
* @since 1.4.0
*/
public static Builder withPassphrase(CharSequence passphrase) {
return new Builder().withPassphrase(passphrase);
}

/**
* Starts construction of {@code CryptoFileSystemProperties}
*
Expand Down Expand Up @@ -217,7 +243,7 @@ private <T> void checkedSet(Class<T> type, String key, Map<String, ?> properties
* @return this
*/
public Builder withPassphrase(CharSequence passphrase) {
this.passphrase = passphrase;
this.passphrase = Normalizer.normalize(passphrase, Form.NFC);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.nio.file.StandardOpenOption.CREATE_NEW;
import static java.nio.file.StandardOpenOption.WRITE;
import static org.cryptomator.cryptofs.CryptoFileSystemUri.create;

import java.io.IOException;
import java.net.URI;
Expand All @@ -28,6 +27,7 @@
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException;
import java.nio.file.OpenOption;
import java.nio.file.Path;
Expand All @@ -39,10 +39,13 @@
import java.nio.file.spi.FileSystemProvider;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;

import org.cryptomator.cryptofs.migration.Migrators;
import org.cryptomator.cryptolib.Cryptors;
import org.cryptomator.cryptolib.api.Cryptor;
import org.cryptomator.cryptolib.api.CryptorProvider;
Expand All @@ -66,7 +69,8 @@
* storageLocation,
* {@link CryptoFileSystemProperties cryptoFileSystemProperties()}
* .withPassword("password")
* .withReadonlyFlag().build());
* .withFlags(FileSystemFlags.READONLY)
* .build());
* </pre>
*
* </blockquote>
Expand Down Expand Up @@ -98,8 +102,26 @@ public CryptoFileSystemProvider() {
this.moveOperation = component.moveOperation();
}

public static CryptoFileSystem newFileSystem(Path pathToVault, CryptoFileSystemProperties properties) throws IOException {
return (CryptoFileSystem) FileSystems.newFileSystem(create(pathToVault.toAbsolutePath()), properties);
private static SecureRandom strongSecureRandom() {
try {
return SecureRandom.getInstanceStrong();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("A strong algorithm must exist in every Java platform.", e);
}
}

/**
* Typesafe alternative to {@link FileSystems#newFileSystem(URI, Map)}. Default way to retrieve a CryptoFS instance.
*
* @param pathToVault Path to this vault's storage location
* @param properties Parameters used during initialization of the file system
* @return a new file system
* @throws FileSystemNeedsMigrationException if the vault format needs to get updated and <code>properties</code> did not contain a flag for implicit migration.
* @throws IOException if an I/O error occurs creating the file system
*/
public static CryptoFileSystem newFileSystem(Path pathToVault, CryptoFileSystemProperties properties) throws FileSystemNeedsMigrationException, IOException {
URI uri = CryptoFileSystemUri.create(pathToVault.toAbsolutePath());
return (CryptoFileSystem) FileSystems.newFileSystem(uri, properties);
}

/**
Expand Down Expand Up @@ -134,7 +156,7 @@ public static void initialize(Path pathToVault, String masterkeyFilename, byte[]
try (Cryptor cryptor = CRYPTOR_PROVIDER.createNew()) {
// save masterkey file:
Path masterKeyPath = pathToVault.resolve(masterkeyFilename);
byte[] keyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase, pepper, Constants.VAULT_VERSION).serialize();
byte[] keyFileContents = cryptor.writeKeysToMasterkeyFile(Normalizer.normalize(passphrase, Form.NFC), pepper, Constants.VAULT_VERSION).serialize();
Files.write(masterKeyPath, keyFileContents, CREATE_NEW, WRITE);
// create "d/RO/OTDIRECTORY":
String rootDirHash = cryptor.fileNameCryptor().hashDirectoryId(Constants.ROOT_DIR_ID);
Expand Down Expand Up @@ -166,26 +188,44 @@ public static boolean containsVault(Path pathToVault, String masterkeyFilename)
* @param oldPassphrase Current passphrase
* @param newPassphrase Future passphrase
* @throws InvalidPassphraseException If <code>oldPassphrase</code> can not be used to unlock the vault.
* @throws FileSystemNeedsMigrationException if the vault format needs to get updated.
* @throws IOException If the masterkey could not be read or written.
* @since 1.1.0
* @see #changePassphrase(Path, String, byte[], CharSequence, CharSequence)
*/
public static void changePassphrase(Path pathToVault, String masterkeyFilename, CharSequence oldPassphrase, CharSequence newPassphrase)
throws InvalidPassphraseException, FileSystemNeedsMigrationException, IOException {
changePassphrase(pathToVault, masterkeyFilename, new byte[0], oldPassphrase, newPassphrase);
}

/**
* Changes the passphrase of a vault at the given path.
*
* @param pathToVault Vault directory
* @param masterkeyFilename Name of the masterkey file
* @param pepper An application-specific pepper added to the salt during key-derivation (if applicable)
* @param oldPassphrase Current passphrase
* @param newPassphrase Future passphrase
* @throws InvalidPassphraseException If <code>oldPassphrase</code> can not be used to unlock the vault.
* @throws FileSystemNeedsMigrationException if the vault format needs to get updated.
* @throws IOException If the masterkey could not be read or written.
* @since 1.4.0
*/
public static void changePassphrase(Path pathToVault, String masterkeyFilename, CharSequence oldPassphrase, CharSequence newPassphrase) throws InvalidPassphraseException, IOException {
public static void changePassphrase(Path pathToVault, String masterkeyFilename, byte[] pepper, CharSequence oldPassphrase, CharSequence newPassphrase)
throws InvalidPassphraseException, FileSystemNeedsMigrationException, IOException {
if (Migrators.get().needsMigration(pathToVault, masterkeyFilename)) {
throw new FileSystemNeedsMigrationException(pathToVault);
}
String normalizedOldPassphrase = Normalizer.normalize(oldPassphrase, Form.NFC);
String normalizedNewPassphrase = Normalizer.normalize(newPassphrase, Form.NFC);
Path masterKeyPath = pathToVault.resolve(masterkeyFilename);
Path backupKeyPath = pathToVault.resolve(masterkeyFilename + Constants.MASTERKEY_BACKUP_SUFFIX);
byte[] oldMasterkeyBytes = Files.readAllBytes(masterKeyPath);
byte[] newMasterkeyBytes = Cryptors.changePassphrase(CRYPTOR_PROVIDER, oldMasterkeyBytes, oldPassphrase, newPassphrase);
byte[] newMasterkeyBytes = Cryptors.changePassphrase(CRYPTOR_PROVIDER, oldMasterkeyBytes, pepper, normalizedOldPassphrase, normalizedNewPassphrase);
Files.move(masterKeyPath, backupKeyPath, REPLACE_EXISTING, ATOMIC_MOVE);
Files.write(masterKeyPath, newMasterkeyBytes, CREATE_NEW, WRITE);
}

private static SecureRandom strongSecureRandom() {
try {
return SecureRandom.getInstanceStrong();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("A strong algorithm must exist in every Java platform.", e);
}
}

/**
* @deprecated only for testing
*/
Expand Down Expand Up @@ -214,9 +254,21 @@ public CryptoFileSystem newFileSystem(URI uri, Map<String, ?> rawProperties) thr
CryptoFileSystemUri parsedUri = CryptoFileSystemUri.parse(uri);
CryptoFileSystemProperties properties = CryptoFileSystemProperties.wrap(rawProperties);

// TODO remove implicit initialization in 2.0.0:
if (properties.initializeImplicitly() && !CryptoFileSystemProvider.containsVault(parsedUri.pathToVault(), properties.masterkeyFilename())) {
CryptoFileSystemProvider.initialize(parsedUri.pathToVault(), properties.masterkeyFilename(), properties.passphrase());
if (!CryptoFileSystemProvider.containsVault(parsedUri.pathToVault(), properties.masterkeyFilename())) {
// TODO remove implicit initialization in 2.0.0:
if (properties.initializeImplicitly()) {
CryptoFileSystemProvider.initialize(parsedUri.pathToVault(), properties.masterkeyFilename(), properties.passphrase());
} else {
throw new NoSuchFileException(parsedUri.pathToVault().toString(), null, "Vault not initialized.");
}
}

if (Migrators.get().needsMigration(parsedUri.pathToVault(), properties.masterkeyFilename())) {
if (properties.migrateImplicitly()) {
Migrators.get().migrate(parsedUri.pathToVault(), properties.masterkeyFilename(), properties.passphrase());
} else {
throw new FileSystemNeedsMigrationException(parsedUri.pathToVault());
}
}

return fileSystems.create(parsedUri.pathToVault(), properties);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.cryptomator.cryptofs;

import java.nio.file.FileSystemException;
import java.nio.file.Path;

import org.cryptomator.cryptofs.migration.Migrators;

/**
* Indicates that no file system for a given vault can be created, because the vault has been created with an older version of this library.
*
* @see Migrators
* @since 1.4.0
*/
public class FileSystemNeedsMigrationException extends FileSystemException {

public FileSystemNeedsMigrationException(Path pathToVault) {
super(pathToVault.toString(), null, "File system needs migration to a newer format.");
}

}
29 changes: 29 additions & 0 deletions src/main/java/org/cryptomator/cryptofs/migration/Migration.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.cryptofs.migration;

enum Migration {
/**
* @deprecated for testing only
*/
@Deprecated ZERO_TO_ONE(0),

/**
* Migrates vault format 5 to 6.
*/
FIVE_TO_SIX(5);

private final int applicableVersion;

private Migration(int applicableVersion) {
this.applicableVersion = applicableVersion;
}

public boolean isApplicable(int version) {
return version == applicableVersion;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*******************************************************************************
* Copyright (c) 2017 Skymatic UG (haftungsbeschränkt).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE file.
*******************************************************************************/
package org.cryptomator.cryptofs.migration;

import dagger.Component;

@Component(modules = {MigrationModule.class})
interface MigrationComponent {

Migrators migrators();

}
Loading

0 comments on commit 62949e1

Please sign in to comment.