Skip to content

Commit

Permalink
Merge branch 'release/1.3.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
overheadhunter committed Jun 16, 2017
2 parents 239cd7b + b61c2de commit 7f9583a
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 30 deletions.
38 changes: 23 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,36 @@ For more information on the security details visit [cryptomator.org](https://cry

CryptoFS depends on a Java 8 JRE/JDK. In addition the JCE unlimited strength policy files (needed for 256-bit keys) must be installed.

### Construction
### Vault initialization

```java
Path storageLocation = Paths.get("/home/cryptobot/vault");
Files.createDirectories(storageLocation);
CryptoFileSystemProvider.initialize(storageLocation, "masterkey.cryptomator", "password");
```

### Obtaining a FileSystem instance

You have the option to use the convenience method ``CryptoFileSystemProvider#newFileSystem`` as follows:

```java
Path storageLocation = Paths.get("/home/cryptobot/vault");
FileSystem fileSystem = CryptoFileSystemProvider.newFileSystem(
storageLocation,
CryptoFileSystemProperties.cryptoFileSystemProperties()
.withPassphrase("password")
.withReadonlyFlag() // readonly flag is optional of course
.withFlags(FileSystemFlags.READONLY) // readonly flag is optional of course
.build());
```

or to use one of the standard methods from ``FileSystems#newFileSystem``:

```java
Path storageLocation = Paths.get("/home/cryptobot/vault");
URI uri = CryptoFileSystemUri.create(storageLocation);
FileSystem fileSystem = FileSystems.newFileSystem(
uri,
CryptoFileSystemProperties.cryptoFileSystemProperties()
.withPassphrase("password")
.withReadonlyFlag() // readonly flag is optional of course
.withFlags(FileSystemFlags.READONLY) // readonly flag is optional of course
.build());
```

Expand All @@ -59,20 +65,22 @@ For more details on construction have a look at the javadoc of ``CryptoFileSytem
### Using the constructed file system

```java
FileSystem fileSystem = ...; // see above
try (FileSystem fileSystem = ...) { // see above

// obtain a path to a test file
Path testFile = fileSystem.getPath("/foo/bar/test");

// obtain a path to a test file
Path testFile = fileSystem.getPath("/foo/bar/test");
// create all parent directories
Files.createDirectories(testFile.getParent());

// create all parent directories
Files.createDirectories(testFile.getParent());
// Write data to the file
Files.write(testFile, "test".getBytes());

// Write data to the file
Files.write(testFile, "test".getBytes());
// List all files present in a directory
try (Stream<Path> listing = Files.list(testFile.getParent())) {
listing.forEach(System.out::println);
}

// List all files present in a directory
try (Stream<Path> listing = Files.list(testFile.getParent())) {
listing.forEach(System.out::println);
}
```

Expand Down
2 changes: 1 addition & 1 deletion 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.1</version>
<version>1.3.2</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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public Cryptor provideCryptor(CryptorProvider cryptorProvider, @PathToVault Path
Path backupKeyPath = pathToVault.resolve(properties.masterkeyFilename() + Constants.MASTERKEY_BACKUP_SUFFIX);
assert Files.exists(masterKeyPath); // since 1.3.0 a file system can only be created for existing vaults. initialization is done before.
byte[] keyFileContents = Files.readAllBytes(masterKeyPath);
Cryptor cryptor = cryptorProvider.createFromKeyFile(KeyFile.parse(keyFileContents), properties.passphrase(), Constants.VAULT_VERSION);
Cryptor cryptor = cryptorProvider.createFromKeyFile(KeyFile.parse(keyFileContents), properties.passphrase(), properties.pepper(), Constants.VAULT_VERSION);
Files.copy(masterKeyPath, backupKeyPath, REPLACE_EXISTING);
return cryptor;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ public class CryptoFileSystemProperties extends AbstractMap<String, Object> {
*/
public static final String PROPERTY_PASSPHRASE = "passphrase";

/**
* Key identifying the pepper used during key derivation.
*
* @since 1.3.2
*/
public static final String PROPERTY_PEPPER = "pepper";

static final byte[] DEFAULT_PEPPER = new byte[0];

/**
* Key identifying the name of the masterkey file located inside the vault directory.
*
Expand Down Expand Up @@ -77,6 +86,7 @@ public enum FileSystemFlags {
private CryptoFileSystemProperties(Builder builder) {
this.entries = unmodifiableSet(new HashSet<>(asList( //
entry(PROPERTY_PASSPHRASE, builder.passphrase), //
entry(PROPERTY_PEPPER, builder.pepper), //
entry(PROPERTY_FILESYSTEM_FLAGS, builder.flags), //
entry(PROPERTY_MASTERKEY_FILENAME, builder.masterkeyFilename) //
)));
Expand All @@ -86,6 +96,10 @@ CharSequence passphrase() {
return (CharSequence) get(PROPERTY_PASSPHRASE);
}

byte[] pepper() {
return (byte[]) get(PROPERTY_PEPPER);
}

@SuppressWarnings("unchecked")
Set<FileSystemFlags> flags() {
return (Set<FileSystemFlags>) get(PROPERTY_FILESYSTEM_FLAGS);
Expand Down Expand Up @@ -171,6 +185,7 @@ public static CryptoFileSystemProperties wrap(Map<String, ?> properties) {
public static class Builder {

private CharSequence passphrase;
public byte[] pepper = DEFAULT_PEPPER;
private final Set<FileSystemFlags> flags = EnumSet.copyOf(DEFAULT_FILESYSTEM_FLAGS);
private String masterkeyFilename = DEFAULT_MASTERKEY_FILENAME;

Expand All @@ -179,6 +194,7 @@ private Builder() {

private Builder(Map<String, ?> properties) {
checkedSet(CharSequence.class, PROPERTY_PASSPHRASE, properties, this::withPassphrase);
checkedSet(byte[].class, PROPERTY_PEPPER, properties, this::withPepper);
checkedSet(String.class, PROPERTY_MASTERKEY_FILENAME, properties, this::withMasterkeyFilename);
checkedSet(Set.class, PROPERTY_FILESYSTEM_FLAGS, properties, this::withFlags);
}
Expand All @@ -205,10 +221,36 @@ public Builder withPassphrase(CharSequence passphrase) {
return this;
}

/**
* Sets the pepper for a CryptoFileSystem.
*
* @param pepper A pepper used during key derivation
* @return this
* @since 1.3.2
*/
public Builder withPepper(byte[] pepper) {
this.pepper = pepper;
return this;
}

/**
* Sets the flags for a CryptoFileSystem.
*
* @param flags File system flags
* @return this
* @since 1.3.1
*/
public Builder withFlags(FileSystemFlags... flags) {
return withFlags(asList(flags));
}

/**
* Sets the flags for a CryptoFileSystem.
*
* @param flags collection of file system flags
* @return this
* @since 1.3.0
*/
public Builder withFlags(Collection<FileSystemFlags> flags) {
this.flags.clear();
this.flags.addAll(flags);
Expand All @@ -219,7 +261,9 @@ public Builder withFlags(Collection<FileSystemFlags> flags) {
* Sets the readonly flag for a CryptoFileSystem.
*
* @return this
* @deprecated Will be removed in 2.0.0. Use {@link #withFlags(FileSystemFlags...) withFlags(FileSystemFlags.READONLY)}
*/
@Deprecated
public Builder withReadonlyFlag() {
flags.add(FileSystemFlags.READONLY);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,28 @@ public static CryptoFileSystem newFileSystem(Path pathToVault, CryptoFileSystemP
* @since 1.3.0
*/
public static void initialize(Path pathToVault, String masterkeyFilename, CharSequence passphrase) throws NotDirectoryException, IOException {
initialize(pathToVault, masterkeyFilename, new byte[0], passphrase);
}

/**
* Creates a new vault at the given directory path.
*
* @param pathToVault Path to a not yet existing directory
* @param masterkeyFilename Name of the masterkey file
* @param pepper Application-specific pepper used during key derivation
* @param passphrase Passphrase that should be used to unlock the vault
* @throws NotDirectoryException If the given path is not an existing directory.
* @throws IOException If the vault structure could not be initialized due to I/O errors
* @since 1.3.2
*/
public static void initialize(Path pathToVault, String masterkeyFilename, byte[] pepper, CharSequence passphrase) throws NotDirectoryException, IOException {
if (!Files.isDirectory(pathToVault)) {
throw new NotDirectoryException(pathToVault.toString());
}
try (Cryptor cryptor = CRYPTOR_PROVIDER.createNew()) {
// save masterkey file:
Path masterKeyPath = pathToVault.resolve(masterkeyFilename);
byte[] keyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase, Constants.VAULT_VERSION).serialize();
byte[] keyFileContents = cryptor.writeKeysToMasterkeyFile(passphrase, 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
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package org.cryptomator.cryptofs;

import static org.cryptomator.cryptofs.CryptoFileSystemProperties.DEFAULT_MASTERKEY_FILENAME;
import static org.cryptomator.cryptofs.CryptoFileSystemProperties.DEFAULT_PEPPER;
import static org.cryptomator.cryptofs.CryptoFileSystemProperties.PROPERTY_FILESYSTEM_FLAGS;
import static org.cryptomator.cryptofs.CryptoFileSystemProperties.PROPERTY_MASTERKEY_FILENAME;
import static org.cryptomator.cryptofs.CryptoFileSystemProperties.PROPERTY_PASSPHRASE;
import static org.cryptomator.cryptofs.CryptoFileSystemProperties.PROPERTY_PEPPER;
import static org.cryptomator.cryptofs.CryptoFileSystemProperties.cryptoFileSystemProperties;
import static org.cryptomator.cryptofs.CryptoFileSystemProperties.cryptoFileSystemPropertiesFrom;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

import java.nio.charset.StandardCharsets;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -37,7 +40,7 @@ public void testSetNoPassphrase() {
}

@Test
@SuppressWarnings({"unchecked", "deprecation"})
@SuppressWarnings({ "unchecked", "deprecation" })
public void testSetOnlyPassphrase() {
String passphrase = "aPassphrase";
CryptoFileSystemProperties inTest = cryptoFileSystemProperties() //
Expand All @@ -51,12 +54,13 @@ public void testSetOnlyPassphrase() {
assertThat(inTest.entrySet(),
containsInAnyOrder( //
anEntry(PROPERTY_PASSPHRASE, passphrase), //
anEntry(PROPERTY_PEPPER, DEFAULT_PEPPER), //
anEntry(PROPERTY_MASTERKEY_FILENAME, DEFAULT_MASTERKEY_FILENAME), //
anEntry(PROPERTY_FILESYSTEM_FLAGS, EnumSet.of(FileSystemFlags.INIT_IMPLICITLY))));
}

@Test
@SuppressWarnings({"unchecked", "deprecation"})
@SuppressWarnings({ "unchecked", "deprecation" })
public void testSetPassphraseAndReadonlyFlag() {
String passphrase = "aPassphrase";
CryptoFileSystemProperties inTest = cryptoFileSystemProperties() //
Expand All @@ -71,12 +75,13 @@ public void testSetPassphraseAndReadonlyFlag() {
assertThat(inTest.entrySet(),
containsInAnyOrder( //
anEntry(PROPERTY_PASSPHRASE, passphrase), //
anEntry(PROPERTY_PEPPER, DEFAULT_PEPPER), //
anEntry(PROPERTY_MASTERKEY_FILENAME, DEFAULT_MASTERKEY_FILENAME), //
anEntry(PROPERTY_FILESYSTEM_FLAGS, EnumSet.of(FileSystemFlags.READONLY, FileSystemFlags.INIT_IMPLICITLY))));
}

@Test
@SuppressWarnings({"unchecked", "deprecation"})
@SuppressWarnings({ "unchecked", "deprecation" })
public void testSetPassphraseAndMasterkeyFilenameAndReadonlyFlag() {
String passphrase = "aPassphrase";
String masterkeyFilename = "aMasterkeyFilename";
Expand All @@ -93,17 +98,20 @@ public void testSetPassphraseAndMasterkeyFilenameAndReadonlyFlag() {
assertThat(inTest.entrySet(),
containsInAnyOrder( //
anEntry(PROPERTY_PASSPHRASE, passphrase), //
anEntry(PROPERTY_PEPPER, DEFAULT_PEPPER), //
anEntry(PROPERTY_MASTERKEY_FILENAME, masterkeyFilename), //
anEntry(PROPERTY_FILESYSTEM_FLAGS, EnumSet.of(FileSystemFlags.READONLY, FileSystemFlags.INIT_IMPLICITLY))));
}

@Test
@SuppressWarnings({"unchecked"})
@SuppressWarnings({ "unchecked" })
public void testFromMap() {
Map<String, Object> map = new HashMap<>();
String passphrase = "aPassphrase";
byte[] pepper = "aPepper".getBytes(StandardCharsets.US_ASCII);
String masterkeyFilename = "aMasterkeyFilename";
map.put(PROPERTY_PASSPHRASE, passphrase);
map.put(PROPERTY_PEPPER, pepper);
map.put(PROPERTY_MASTERKEY_FILENAME, masterkeyFilename);
map.put(PROPERTY_FILESYSTEM_FLAGS, EnumSet.of(FileSystemFlags.READONLY));
CryptoFileSystemProperties inTest = cryptoFileSystemPropertiesFrom(map).build();
Expand All @@ -115,6 +123,7 @@ public void testFromMap() {
assertThat(inTest.entrySet(),
containsInAnyOrder( //
anEntry(PROPERTY_PASSPHRASE, passphrase), //
anEntry(PROPERTY_PEPPER, pepper), //
anEntry(PROPERTY_MASTERKEY_FILENAME, masterkeyFilename), //
anEntry(PROPERTY_FILESYSTEM_FLAGS, EnumSet.of(FileSystemFlags.READONLY))));
}
Expand All @@ -124,8 +133,10 @@ public void testFromMap() {
public void testWrapMapWithTrueReadonly() {
Map<String, Object> map = new HashMap<>();
String passphrase = "aPassphrase";
byte[] pepper = "aPepper".getBytes(StandardCharsets.US_ASCII);
String masterkeyFilename = "aMasterkeyFilename";
map.put(PROPERTY_PASSPHRASE, passphrase);
map.put(PROPERTY_PEPPER, pepper);
map.put(PROPERTY_MASTERKEY_FILENAME, masterkeyFilename);
map.put(PROPERTY_FILESYSTEM_FLAGS, EnumSet.of(FileSystemFlags.READONLY));
CryptoFileSystemProperties inTest = CryptoFileSystemProperties.wrap(map);
Expand All @@ -137,6 +148,7 @@ public void testWrapMapWithTrueReadonly() {
assertThat(inTest.entrySet(),
containsInAnyOrder( //
anEntry(PROPERTY_PASSPHRASE, passphrase), //
anEntry(PROPERTY_PEPPER, pepper), //
anEntry(PROPERTY_MASTERKEY_FILENAME, masterkeyFilename), //
anEntry(PROPERTY_FILESYSTEM_FLAGS, EnumSet.of(FileSystemFlags.READONLY))));
}
Expand All @@ -146,8 +158,10 @@ public void testWrapMapWithTrueReadonly() {
public void testWrapMapWithFalseReadonly() {
Map<String, Object> map = new HashMap<>();
String passphrase = "aPassphrase";
byte[] pepper = "aPepper".getBytes(StandardCharsets.US_ASCII);
String masterkeyFilename = "aMasterkeyFilename";
map.put(PROPERTY_PASSPHRASE, passphrase);
map.put(PROPERTY_PEPPER, pepper);
map.put(PROPERTY_MASTERKEY_FILENAME, masterkeyFilename);
map.put(PROPERTY_FILESYSTEM_FLAGS, EnumSet.noneOf(FileSystemFlags.class));
CryptoFileSystemProperties inTest = CryptoFileSystemProperties.wrap(map);
Expand All @@ -159,6 +173,7 @@ public void testWrapMapWithFalseReadonly() {
assertThat(inTest.entrySet(),
containsInAnyOrder( //
anEntry(PROPERTY_PASSPHRASE, passphrase), //
anEntry(PROPERTY_PEPPER, pepper), //
anEntry(PROPERTY_MASTERKEY_FILENAME, masterkeyFilename), //
anEntry(PROPERTY_FILESYSTEM_FLAGS, EnumSet.noneOf(FileSystemFlags.class))));
}
Expand Down Expand Up @@ -197,11 +212,13 @@ public void testWrapMapWithInvalidPassphrase() {
}

@Test
@SuppressWarnings({"unchecked", "deprecation"})
@SuppressWarnings({ "unchecked", "deprecation" })
public void testWrapMapWithoutReadonly() {
Map<String, Object> map = new HashMap<>();
String passphrase = "aPassphrase";
byte[] pepper = "aPepper".getBytes(StandardCharsets.US_ASCII);
map.put(PROPERTY_PASSPHRASE, passphrase);
map.put(PROPERTY_PEPPER, pepper);
CryptoFileSystemProperties inTest = CryptoFileSystemProperties.wrap(map);

assertThat(inTest.passphrase(), is(passphrase));
Expand All @@ -211,6 +228,7 @@ public void testWrapMapWithoutReadonly() {
assertThat(inTest.entrySet(),
containsInAnyOrder( //
anEntry(PROPERTY_PASSPHRASE, passphrase), //
anEntry(PROPERTY_PEPPER, pepper), //
anEntry(PROPERTY_MASTERKEY_FILENAME, DEFAULT_MASTERKEY_FILENAME), //
anEntry(PROPERTY_FILESYSTEM_FLAGS, EnumSet.of(FileSystemFlags.INIT_IMPLICITLY))));
}
Expand Down
Loading

0 comments on commit 7f9583a

Please sign in to comment.