- support for long filenames

- increased thread count for webdav server
- fixed severe bug that allowed using non-random masterkeys (now throwing exceptions if this attempt is made)
This commit is contained in:
Sebastian Stenzel 2014-12-06 14:31:55 +01:00
parent 8ae7e95c41
commit ce197b3314
39 changed files with 990 additions and 112 deletions

View File

@ -28,11 +28,11 @@ public final class WebDAVServer {
private static final Logger LOG = LoggerFactory.getLogger(WebDAVServer.class);
private static final String LOCALHOST = "127.0.0.1";
private static final int MAX_PENDING_REQUESTS = 200;
private static final int MAX_THREADS = 4;
private static final int MIN_THREADS = 2;
private static final int MAX_THREADS = 200;
private static final int MIN_THREADS = 4;
private static final int THREAD_IDLE_SECONDS = 20;
private final Server server;
public WebDAVServer() {
final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(MAX_PENDING_REQUESTS);
final ThreadPool tp = new QueuedThreadPool(MAX_THREADS, MIN_THREADS, THREAD_IDLE_SECONDS, queue);

View File

@ -8,26 +8,32 @@
******************************************************************************/
package org.cryptomator.webdav.jackrabbit;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import org.apache.commons.collections4.BidiMap;
import org.apache.jackrabbit.webdav.AbstractLocatorFactory;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.crypto.CryptorIOSupport;
import org.cryptomator.crypto.SensitiveDataSwipeListener;
public class WebDavLocatorFactory extends AbstractLocatorFactory implements SensitiveDataSwipeListener {
public class WebDavLocatorFactory extends AbstractLocatorFactory implements SensitiveDataSwipeListener, CryptorIOSupport {
private static final int MAX_CACHED_PATHS = 10000;
private final Path fsRoot;
private final Cryptor cryptor;
private final BidiLRUMap<String, String> pathCache; // <decryptedPath, encryptedPath>
private final BidiMap<String, String> pathCache = new BidiLRUMap<>(MAX_CACHED_PATHS); // <decryptedPath, encryptedPath>
public WebDavLocatorFactory(String fsRoot, String httpRoot, Cryptor cryptor) {
super(httpRoot);
this.fsRoot = FileSystems.getDefault().getPath(fsRoot);
this.cryptor = cryptor;
this.pathCache = new BidiLRUMap<>(MAX_CACHED_PATHS);
cryptor.addSensitiveDataSwipeListener(this);
}
@ -48,7 +54,7 @@ public class WebDavLocatorFactory extends AbstractLocatorFactory implements Sens
if (resourcePath == null) {
return fsRoot.toString();
}
final String encryptedRepoPath = cryptor.encryptPath(resourcePath, FileSystems.getDefault().getSeparator().charAt(0), '/');
final String encryptedRepoPath = cryptor.encryptPath(resourcePath, FileSystems.getDefault().getSeparator().charAt(0), '/', this);
return fsRoot.resolve(encryptedRepoPath).toString();
}
@ -71,7 +77,7 @@ public class WebDavLocatorFactory extends AbstractLocatorFactory implements Sens
return null;
} else {
final Path relativeRepositoryPath = fsRoot.relativize(absRepoPath);
final String resourcePath = cryptor.decryptPath(relativeRepositoryPath.toString(), FileSystems.getDefault().getSeparator().charAt(0), '/');
final String resourcePath = cryptor.decryptPath(relativeRepositoryPath.toString(), FileSystems.getDefault().getSeparator().charAt(0), '/', this);
return resourcePath;
}
}
@ -93,4 +99,36 @@ public class WebDavLocatorFactory extends AbstractLocatorFactory implements Sens
pathCache.clear();
}
/* Cryptor I/O Support */
@Override
public void writePathSpecificMetadata(String encryptedPath, byte[] encryptedMetadata) throws IOException {
final Path metaDataFile = fsRoot.resolve(encryptedPath);
final SeekableByteChannel channel = Files.newByteChannel(metaDataFile, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.DSYNC);
try {
final ByteBuffer buffer = ByteBuffer.wrap(encryptedMetadata);
while (channel.write(buffer) > 0) {
// continue writing.
}
} finally {
channel.close();
}
}
@Override
public byte[] readPathSpecificMetadata(String encryptedPath) throws IOException {
final Path metaDataFile = fsRoot.resolve(encryptedPath);
final long metaDataFileSize = Files.size(metaDataFile);
final SeekableByteChannel channel = Files.newByteChannel(metaDataFile, StandardOpenOption.READ);
try {
final ByteBuffer buffer = ByteBuffer.allocate((int) metaDataFileSize);
while (channel.read(buffer) > 0) {
// continue reading.
}
return buffer.array();
} finally {
channel.close();
}
}
}

View File

@ -26,6 +26,7 @@ import java.security.spec.KeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@ -40,8 +41,10 @@ import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.AbstractCryptor;
import org.cryptomator.crypto.CryptorIOSupport;
import org.cryptomator.crypto.exceptions.DecryptFailedException;
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
import org.cryptomator.crypto.exceptions.WrongPasswordException;
@ -73,6 +76,11 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
*/
private static final int AES_KEY_LENGTH;
/**
*
*/
private static final byte[] EMPTY_MASTER_KEY = new byte[MASTER_KEY_LENGTH];
/**
* Jackson JSON-Mapper.
*/
@ -82,7 +90,7 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
* The decrypted master key. Its lifecycle starts with {@link #randomData(int)} or {@link #encryptMasterKey(Path, CharSequence)}. Its
* lifecycle ends with {@link #swipeSensitiveData()}.
*/
private final byte[] masterKey = new byte[MASTER_KEY_LENGTH];
private final byte[] masterKey = Arrays.copyOf(EMPTY_MASTER_KEY, MASTER_KEY_LENGTH);
private static final int SIZE_OF_LONG = Long.SIZE / Byte.SIZE;
private static final int SIZE_OF_INT = Integer.SIZE / Byte.SIZE;
@ -110,6 +118,9 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
* Encrypts the current masterKey with the given password and writes the result to the given output stream.
*/
public void encryptMasterKey(OutputStream out, CharSequence password) throws IOException {
if (ArrayUtils.isEquals(this.masterKey, EMPTY_MASTER_KEY)) {
throw new IllegalStateException("Masterkey not yet initialized.");
}
try {
// derive key:
final byte[] userSalt = randomData(SALT_LENGTH);
@ -246,24 +257,24 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
}
@Override
public String encryptPath(String cleartextPath, char encryptedPathSep, char cleartextPathSep) {
public String encryptPath(String cleartextPath, char encryptedPathSep, char cleartextPathSep, CryptorIOSupport ioSupport) {
try {
final SecretKey key = this.pbkdf2(masterKey, EMPTY_SALT, PBKDF2_MASTERKEY_ITERATIONS, AES_KEY_LENGTH);
final String[] cleartextPathComps = StringUtils.split(cleartextPath, cleartextPathSep);
final List<String> encryptedPathComps = new ArrayList<>(cleartextPathComps.length);
for (final String cleartext : cleartextPathComps) {
final String encrypted = encryptPathComponent(cleartext, key);
final String encrypted = encryptPathComponent(cleartext, key, ioSupport);
encryptedPathComps.add(encrypted);
}
return StringUtils.join(encryptedPathComps, encryptedPathSep);
} catch (IllegalBlockSizeException | BadPaddingException e) {
} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
throw new IllegalStateException("Unable to encrypt path: " + cleartextPath, e);
}
}
private String encryptPathComponent(final String cleartext, final SecretKey key) throws IllegalBlockSizeException, BadPaddingException {
private String encryptPathComponent(final String cleartext, final SecretKey key, CryptorIOSupport ioSupport) throws IllegalBlockSizeException, BadPaddingException, IOException {
if (cleartext.length() > PLAINTEXT_FILENAME_LENGTH_LIMIT) {
return encryptLongPathComponent(cleartext, key);
return encryptLongPathComponent(cleartext, key, ioSupport);
} else {
return encryptShortPathComponent(cleartext, key);
}
@ -275,29 +286,35 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
return ENCRYPTED_FILENAME_CODEC.encodeAsString(encryptedBytes) + BASIC_FILE_EXT;
}
private String encryptLongPathComponent(String cleartext, SecretKey key) {
throw new UnsupportedOperationException("not yet implemented");
private String encryptLongPathComponent(final String cleartext, final SecretKey key, CryptorIOSupport ioSupport) throws IllegalBlockSizeException, BadPaddingException, IOException {
final Cipher cipher = this.cipher(FILE_NAME_CIPHER, key, EMPTY_IV, Cipher.ENCRYPT_MODE);
final byte[] encryptedBytes = cipher.doFinal(cleartext.getBytes(Charsets.UTF_8));
final LongFilenameMetadata metadata = new LongFilenameMetadata();
metadata.setEncryptedFilename(ENCRYPTED_FILENAME_CODEC.encodeAsString(encryptedBytes));
final String alternativeFileName = UUID.randomUUID().toString() + LONG_NAME_FILE_EXT;
ioSupport.writePathSpecificMetadata(alternativeFileName + METADATA_FILE_EXT, objectMapper.writeValueAsBytes(metadata));
return alternativeFileName;
}
@Override
public String decryptPath(String encryptedPath, char encryptedPathSep, char cleartextPathSep) {
public String decryptPath(String encryptedPath, char encryptedPathSep, char cleartextPathSep, CryptorIOSupport ioSupport) {
try {
final SecretKey key = this.pbkdf2(masterKey, EMPTY_SALT, PBKDF2_MASTERKEY_ITERATIONS, AES_KEY_LENGTH);
final String[] encryptedPathComps = StringUtils.split(encryptedPath, encryptedPathSep);
final List<String> cleartextPathComps = new ArrayList<>(encryptedPathComps.length);
for (final String encrypted : encryptedPathComps) {
final String cleartext = decryptPathComponent(encrypted, key);
final String cleartext = decryptPathComponent(encrypted, key, ioSupport);
cleartextPathComps.add(new String(cleartext));
}
return StringUtils.join(cleartextPathComps, cleartextPathSep);
} catch (IllegalBlockSizeException | BadPaddingException e) {
} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
throw new IllegalStateException("Unable to decrypt path: " + encryptedPath, e);
}
}
private String decryptPathComponent(final String encrypted, final SecretKey key) throws IllegalBlockSizeException, BadPaddingException {
private String decryptPathComponent(final String encrypted, final SecretKey key, CryptorIOSupport ioSupport) throws IllegalBlockSizeException, BadPaddingException, IOException {
if (encrypted.endsWith(LONG_NAME_FILE_EXT)) {
return decryptLongPathComponent(encrypted, key);
return decryptLongPathComponent(encrypted, key, ioSupport);
} else if (encrypted.endsWith(BASIC_FILE_EXT)) {
return decryptShortPathComponent(encrypted, key);
} else {
@ -313,8 +330,12 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
return new String(cleartextBytes, Charsets.UTF_8);
}
private String decryptLongPathComponent(final String encrypted, final SecretKey key) {
throw new UnsupportedOperationException("not yet implemented");
private String decryptLongPathComponent(final String encrypted, final SecretKey key, CryptorIOSupport ioSupport) throws IllegalBlockSizeException, BadPaddingException, IOException {
final LongFilenameMetadata metadata = objectMapper.readValue(ioSupport.readPathSpecificMetadata(encrypted + METADATA_FILE_EXT), LongFilenameMetadata.class);
final Cipher cipher = this.cipher(FILE_NAME_CIPHER, key, EMPTY_IV, Cipher.DECRYPT_MODE);
final byte[] encryptedBytes = ENCRYPTED_FILENAME_CODEC.decode(metadata.getEncryptedFilename());
final byte[] cleartextBytes = cipher.doFinal(encryptedBytes);
return new String(cleartextBytes, Charsets.UTF_8);
}
@Override

View File

@ -16,10 +16,9 @@ interface AesCryptographicConfiguration {
int PRNG_SEED_LENGTH = 16;
/**
* Number of bytes of the master key. Should be significantly higher than the {@link #AES_KEY_LENGTH}, as a corrupted masterkey can't be
* changed without decrypting and re-encrypting all files first.
* Number of bytes of the master key. Should be the maximum possible AES key length to provide best security.
*/
int MASTER_KEY_LENGTH = 512;
int MASTER_KEY_LENGTH = 256;
/**
* Number of bytes used as salt, where needed.

View File

@ -45,6 +45,11 @@ interface FileNamingConventions {
*/
String LONG_NAME_FILE_EXT = ".lng.aes";
/**
* For file-related metadata.
*/
String METADATA_FILE_EXT = ".meta";
/**
* Matches both, {@value #BASIC_FILE_EXT} and {@value #LONG_NAME_FILE_EXT} files.
*/

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2014 Sebastian Stenzel
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.crypto.aes256;
import java.io.Serializable;
class LongFilenameMetadata implements Serializable {
private static final long serialVersionUID = 6214509403824421320L;
private String encryptedFilename;
/* Getter/Setter */
public String getEncryptedFilename() {
return encryptedFilename;
}
public void setEncryptedFilename(String encryptedFilename) {
this.encryptedFilename = encryptedFilename;
}
}

View File

@ -1,79 +0,0 @@
/*******************************************************************************
* Copyright (c) 2014 Sebastian Stenzel
* This file is licensed under the terms of the MIT license.
* See the LICENSE.txt file for more info.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
******************************************************************************/
package org.cryptomator.crypto.aes256;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.collections4.BidiMap;
import org.apache.commons.collections4.bidimap.DualHashBidiMap;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
@JsonPropertyOrder(value = { "iv", "salt", "files" })
class Metadata implements Serializable {
private static final long serialVersionUID = 6214509403824421320L;
private byte[] iv;
private byte[] salt;
@JsonDeserialize(as = DualHashBidiMap.class)
private BidiMap<String, byte[]> filenames;
private Map<String, Long> filesizes;
Metadata() {
// used by jackson
}
Metadata(byte[] iv, byte[] salt) {
this.iv = iv;
this.salt = salt;
}
/* Getter/Setter */
public byte[] getIv() {
return iv;
}
public void setIv(byte[] iv) {
this.iv = iv;
}
public byte[] getSalt() {
return salt;
}
public void setSalt(byte[] salt) {
this.salt = salt;
}
public BidiMap<String, byte[]> getFilenames() {
if (filenames == null) {
filenames = new DualHashBidiMap<>();
}
return filenames;
}
public void setFilenames(BidiMap<String, byte[]> filesnames) {
this.filenames = filesnames;
}
public Map<String, Long> getFilesizes() {
if (filesizes == null) {
filesizes = new HashMap<>();
}
return filesizes;
}
public void setFilesizes(Map<String, Long> filesizes) {
this.filesizes = filesizes;
}
}

View File

@ -17,13 +17,16 @@ import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.cryptomator.crypto.aes256.Aes256Cryptor;
import org.cryptomator.crypto.CryptorIOSupport;
import org.cryptomator.crypto.exceptions.DecryptFailedException;
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
import org.cryptomator.crypto.exceptions.WrongPasswordException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -47,11 +50,20 @@ public class Aes256CryptorTest {
/* ------------------------------------------------------------------------------- */
@Test(expected = IllegalStateException.class)
public void testUninitializedMasterKey() throws IOException {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor();
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
cryptor.encryptMasterKey(out, pw);
}
@Test
public void testCorrectPassword() throws IOException, WrongPasswordException, DecryptFailedException, UnsupportedKeyLengthException {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor();
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
cryptor.randomizeMasterKey();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@ -65,6 +77,7 @@ public class Aes256CryptorTest {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor();
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
cryptor.randomizeMasterKey();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@ -79,6 +92,7 @@ public class Aes256CryptorTest {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor();
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
cryptor.randomizeMasterKey();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@ -93,6 +107,7 @@ public class Aes256CryptorTest {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor();
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
cryptor.randomizeMasterKey();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@ -101,4 +116,40 @@ public class Aes256CryptorTest {
cryptor.swipeSensitiveData();
}
@Test
public void testEncryptionOfFilenames() throws IOException {
final CryptorIOSupport ioSupportMock = new CryptoIOSupportMock();
final Aes256Cryptor cryptor = new Aes256Cryptor();
cryptor.randomizeMasterKey();
// short path components
final String originalPath1 = "foo/bar/baz";
final String encryptedPath1 = cryptor.encryptPath(originalPath1, '/', '/', ioSupportMock);
final String decryptedPath1 = cryptor.decryptPath(encryptedPath1, '/', '/', ioSupportMock);
Assert.assertEquals(originalPath1, decryptedPath1);
// long path components
final String str50chars = "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee";
final String originalPath2 = "foo/" + str50chars + str50chars + str50chars + str50chars + str50chars + "/baz";
final String encryptedPath2 = cryptor.encryptPath(originalPath2, '/', '/', ioSupportMock);
final String decryptedPath2 = cryptor.decryptPath(encryptedPath2, '/', '/', ioSupportMock);
Assert.assertEquals(originalPath2, decryptedPath2);
}
private static class CryptoIOSupportMock implements CryptorIOSupport {
private final Map<String, byte[]> map = new HashMap<>();
@Override
public void writePathSpecificMetadata(String encryptedPath, byte[] encryptedMetadata) {
map.put(encryptedPath, encryptedMetadata);
}
@Override
public byte[] readPathSpecificMetadata(String encryptedPath) {
return map.get(encryptedPath);
}
}
}

View File

@ -0,0 +1,71 @@
<html>
<head>
<title>TestNG: Default test</title>
<link href="../testng.css" rel="stylesheet" type="text/css" />
<link href="../my-testng.css" rel="stylesheet" type="text/css" />
<style type="text/css">
.log { display: none;}
.stack-trace { display: none;}
</style>
<script type="text/javascript">
<!--
function flip(e) {
current = e.style.display;
if (current == 'block') {
e.style.display = 'none';
return 0;
}
else {
e.style.display = 'block';
return 1;
}
}
function toggleBox(szDivId, elem, msg1, msg2)
{
var res = -1; if (document.getElementById) {
res = flip(document.getElementById(szDivId));
}
else if (document.all) {
// this is the way old msie versions work
res = flip(document.all[szDivId]);
}
if(elem) {
if(res == 0) elem.innerHTML = msg1; else elem.innerHTML = msg2;
}
}
function toggleAllBoxes() {
if (document.getElementsByTagName) {
d = document.getElementsByTagName('div');
for (i = 0; i < d.length; i++) {
if (d[i].className == 'log') {
flip(d[i]);
}
}
}
}
// -->
</script>
</head>
<body>
<h2 align='center'>Default test</h2><table border='1' align="center">
<tr>
<td>Tests passed/Failed/Skipped:</td><td>0/0/0</td>
</tr><tr>
<td>Started on:</td><td>Sat Dec 06 14:26:26 CET 2014</td>
</tr>
<tr><td>Total time:</td><td>0 seconds (5 ms)</td>
</tr><tr>
<td>Included groups:</td><td></td>
</tr><tr>
<td>Excluded groups:</td><td></td>
</tr>
</table><p/>
<small><i>(Hover the method name to see the test class name)</i></small><p/>
</body>
</html>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated by org.testng.reporters.JUnitXMLReporter -->
<testsuite hostname="Sebastians-iMac.local" tests="0" failures="0" timestamp="6 Dec 2014 13:26:27 GMT" time="0.005" errors="0">
</testsuite>

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

View File

@ -0,0 +1,2 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>TestNG Report</title><style type="text/css">table {margin-bottom:10px;border-collapse:collapse;empty-cells:show}th,td {border:1px solid #009;padding:.25em .5em}th {vertical-align:bottom}td {vertical-align:top}table a {font-weight:bold}.stripe td {background-color: #E6EBF9}.num {text-align:right}.passedodd td {background-color: #3F3}.passedeven td {background-color: #0A0}.skippedodd td {background-color: #DDD}.skippedeven td {background-color: #CCC}.failedodd td,.attn {background-color: #F33}.failedeven td,.stripe .attn {background-color: #D00}.stacktrace {white-space:pre;font-family:monospace}.totop {font-size:85%;text-align:center;border-bottom:2px solid #000}</style></head><body><table><tr><th>Test</th><th># Passed</th><th># Skipped</th><th># Failed</th><th>Time (ms)</th><th>Included Groups</th><th>Excluded Groups</th></tr><tr><th colspan="7">Default suite</th></tr><tr><td><a href="#t0">Default test</a></td><td class="num">0</td><td class="num">0</td><td class="num">0</td><td class="num">5</td><td></td><td></td></tr></table><table><thead><tr><th>Class</th><th>Method</th><th>Start</th><th>Time (ms)</th></tr></thead><tbody><tr><th colspan="4">Default suite</th></tr></tbody><tbody id="t0"></tbody></table><h2>Default test</h2></body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

View File

@ -0,0 +1,195 @@
<!DOCTYPE html>
<html>
<head>
<title>TestNG reports</title>
<link type="text/css" href="testng-reports.css" rel="stylesheet" />
<script type="text/javascript" src="jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="testng-reports.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type='text/javascript'>
google.load('visualization', '1', {packages:['table']});
google.setOnLoadCallback(drawTable);
var suiteTableInitFunctions = new Array();
var suiteTableData = new Array();
</script>
<!--
<script type="text/javascript" src="jquery-ui/js/jquery-ui-1.8.16.custom.min.js"></script>
-->
</head>
<body>
<div class="top-banner-root">
<span class="top-banner-title-font">Test results</span>
<br/>
<span class="top-banner-font-1">1 suite</span>
</div> <!-- top-banner-root -->
<div class="navigator-root">
<div class="navigator-suite-header">
<span>All suites</span>
<a href="#" class="collapse-all-link" title="Collapse/expand all the suites">
<img class="collapse-all-icon" src="collapseall.gif">
</img> <!-- collapse-all-icon -->
</a> <!-- collapse-all-link -->
</div> <!-- navigator-suite-header -->
<div class="suite">
<div class="rounded-window">
<div class="suite-header light-rounded-window-top">
<a href="#" class="navigator-link" panel-name="suite-Default_suite">
<span class="suite-name border-passed">Default suite</span>
</a> <!-- navigator-link -->
</div> <!-- suite-header light-rounded-window-top -->
<div class="navigator-suite-content">
<div class="suite-section-title">
<span>Info</span>
</div> <!-- suite-section-title -->
<div class="suite-section-content">
<ul>
<li>
<a href="#" class="navigator-link " panel-name="test-xml-Default_suite">
<span>testng-customsuite.xml</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" class="navigator-link " panel-name="testlist-Default_suite">
<span class="test-stats">1 test</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" class="navigator-link " panel-name="group-Default_suite">
<span>0 groups</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" class="navigator-link " panel-name="times-Default_suite">
<span>Times</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" class="navigator-link " panel-name="reporter-Default_suite">
<span>Reporter output</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" class="navigator-link " panel-name="ignored-methods-Default_suite">
<span>Ignored methods</span>
</a> <!-- navigator-link -->
</li>
<li>
<a href="#" class="navigator-link " panel-name="chronological-Default_suite">
<span>Chronological view</span>
</a> <!-- navigator-link -->
</li>
</ul>
</div> <!-- suite-section-content -->
<div class="result-section">
<div class="suite-section-title">
<span>Results</span>
</div> <!-- suite-section-title -->
<div class="suite-section-content">
<ul>
<li>
<span class="method-stats">0 methods, </span>
</li>
</ul>
</div> <!-- suite-section-content -->
</div> <!-- result-section -->
</div> <!-- navigator-suite-content -->
</div> <!-- rounded-window -->
</div> <!-- suite -->
</div> <!-- navigator-root -->
<div class="wrapper">
<div class="main-panel-root">
<div panel-name="suite-Default_suite" class="panel Default_suite">
</div> <!-- panel Default_suite -->
<div panel-name="test-xml-Default_suite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">/private/var/folders/t_/sydpw2q97yj_fh3p7jp6jx8w0000gn/T/testng-eclipse-1690973351/testng-customsuite.xml</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
<pre>
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE suite SYSTEM &quot;http://testng.org/testng-1.0.dtd&quot;&gt;
&lt;suite name=&quot;Default suite&quot;&gt;
&lt;test verbose=&quot;2&quot; name=&quot;Default test&quot;&gt;
&lt;classes&gt;
&lt;class name=&quot;org.cryptomator.crypto.aes256.Aes256CryptorTest&quot;&gt;
&lt;methods&gt;
&lt;include name=&quot;testEncryptionOfLongFilenames&quot;/&gt;
&lt;/methods&gt;
&lt;/class&gt; &lt;!-- org.cryptomator.crypto.aes256.Aes256CryptorTest --&gt;
&lt;/classes&gt;
&lt;/test&gt; &lt;!-- Default test --&gt;
&lt;/suite&gt; &lt;!-- Default suite --&gt;
</pre>
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="testlist-Default_suite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Tests for Default suite</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
<ul>
<li>
<span class="test-name">Default test (1 class)</span>
</li>
</ul>
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="group-Default_suite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Groups for Default suite</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="times-Default_suite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Times for Default suite</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
<div class="times-div">
<script type="text/javascript">
suiteTableInitFunctions.push('tableData_Default_suite');
function tableData_Default_suite() {
var data = new google.visualization.DataTable();
data.addColumn('number', 'Number');
data.addColumn('string', 'Method');
data.addColumn('string', 'Class');
data.addColumn('number', 'Time (ms)');
data.addRows(0);
window.suiteTableData['Default_suite']= { tableData: data, tableDiv: 'times-div-Default_suite'}
return data;
}
</script>
<div id="times-div-Default_suite">
</div> <!-- times-div-Default_suite -->
</div> <!-- times-div -->
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="reporter-Default_suite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Reporter output for Default suite</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="ignored-methods-Default_suite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">0 ignored methods</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
<div panel-name="chronological-Default_suite" class="panel">
<div class="main-panel-header rounded-window-top">
<span class="header-content">Methods in chronological order</span>
</div> <!-- main-panel-header rounded-window-top -->
<div class="main-panel-content rounded-window-bottom">
</div> <!-- main-panel-content rounded-window-bottom -->
</div> <!-- panel -->
</div> <!-- main-panel-root -->
</div> <!-- wrapper -->
</body>
</html>

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

View File

@ -0,0 +1 @@
[SuiteResult context=Default test]

View File

@ -0,0 +1,6 @@
<table border='1'>
<tr>
<th>Class name</th>
<th>Method name</th>
<th>Groups</th>
</tr></table>

View File

@ -0,0 +1 @@
<h2>Groups used for this test run</h2>

View File

@ -0,0 +1,6 @@
<html><head><title>Results for Default suite</title></head>
<frameset cols="26%,74%">
<frame src="toc.html" name="navFrame">
<frame src="main.html" name="mainFrame">
</frameset>
</html>

View File

@ -0,0 +1,2 @@
<html><head><title>Results for Default suite</title></head>
<body>Select a result on the left-hand pane.</body></html>

View File

@ -0,0 +1,2 @@
<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>Default suite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>
</table>

View File

@ -0,0 +1,2 @@
<h2>Methods that were not run</h2><table>
</table>

View File

@ -0,0 +1,2 @@
<h2>Methods run, sorted chronologically</h2><h3>&gt;&gt; means before, &lt;&lt; means after</h3><p/><br/><em>Default suite</em><p/><small><i>(Hover the method name to see the test class name)</i></small><p/>
</table>

View File

@ -0,0 +1 @@
<h2>Reporter output</h2><table></table>

View File

@ -0,0 +1 @@
<html><head><title>testng.xml for Default suite</title></head><body><tt>&lt;?xml&nbsp;version="1.0"&nbsp;encoding="UTF-8"?&gt;<br/>&lt;!DOCTYPE&nbsp;suite&nbsp;SYSTEM&nbsp;"http://testng.org/testng-1.0.dtd"&gt;<br/>&lt;suite&nbsp;name="Default&nbsp;suite"&gt;<br/>&nbsp;&nbsp;&lt;test&nbsp;verbose="2"&nbsp;name="Default&nbsp;test"&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;classes&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;class&nbsp;name="org.cryptomator.crypto.aes256.Aes256CryptorTest"&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;methods&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;include&nbsp;name="testEncryptionOfLongFilenames"/&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/methods&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/class&gt;&nbsp;&lt;!--&nbsp;org.cryptomator.crypto.aes256.Aes256CryptorTest&nbsp;--&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/classes&gt;<br/>&nbsp;&nbsp;&lt;/test&gt;&nbsp;&lt;!--&nbsp;Default&nbsp;test&nbsp;--&gt;<br/>&lt;/suite&gt;&nbsp;&lt;!--&nbsp;Default&nbsp;suite&nbsp;--&gt;<br/></tt></body></html>

View File

@ -0,0 +1,30 @@
<html>
<head>
<title>Results for Default suite</title>
<link href="../testng.css" rel="stylesheet" type="text/css" />
<link href="../my-testng.css" rel="stylesheet" type="text/css" />
</head>
<body>
<h3><p align="center">Results for<br/><em>Default suite</em></p></h3>
<table border='1' width='100%'>
<tr valign='top'>
<td>1 test</td>
<td><a target='mainFrame' href='classes.html'>0 class</a></td>
<td>0 method:<br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods.html'>chronological</a><br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods-alphabetical.html'>alphabetical</a><br/>
&nbsp;&nbsp;<a target='mainFrame' href='methods-not-run.html'>not run (0)</a></td>
</tr>
<tr>
<td><a target='mainFrame' href='groups.html'>0 group</a></td>
<td><a target='mainFrame' href='reporter-output.html'>reporter output</a></td>
<td><a target='mainFrame' href='testng.xml.html'>testng.xml</a></td>
</tr></table>
<table width='100%' class='test-failed'>
<tr><td>
<table style='width: 100%'><tr><td valign='top'>Default test (0/0/0)</td><td valign='top' align='right'>
<a href='Default test.html' target='mainFrame'>Results</a>
</td></tr></table>
</td></tr><p/>
</table>
</body></html>

View File

@ -0,0 +1,9 @@
<html>
<head><title>Test results</title><link href="./testng.css" rel="stylesheet" type="text/css" />
<link href="./my-testng.css" rel="stylesheet" type="text/css" />
</head><body>
<h2><p align='center'>Test results</p></h2>
<table border='1' width='100%' class='main-page'><tr><th>Suite</th><th>Passed</th><th>Failed</th><th>Skipped</th><th>testng.xml</th></tr>
<tr align='center' class='invocation-failed'><td><em>Total</em></td><td><em>0</em></td><td><em>0</em></td><td><em>0</em></td><td>&nbsp;</td></tr>
<tr align='center' class='invocation-failed'><td><a href='Default suite/index.html'>Default suite</a></td>
<td>0</td><td>0</td><td>0</td><td><a href='Default suite/testng.xml.html'>Link</a></td></tr></table></body></html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

View File

@ -0,0 +1,309 @@
body {
margin: 0px 0px 5px 5px;
}
ul {
margin: 0px;
}
li {
list-style-type: none;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.navigator-selected {
background: #ffa500;
}
.wrapper {
position: absolute;
top: 60px;
bottom: 0;
left: 400px;
right: 0;
overflow: auto;
}
.navigator-root {
position: absolute;
top: 60px;
bottom: 0;
left: 0;
width: 400px;
overflow-y: auto;
}
.suite {
margin: 0px 10px 10px 0px;
background-color: #fff8dc;
}
.suite-name {
padding-left: 10px;
font-size: 25px;
font-family: Times;
}
.main-panel-header {
padding: 5px;
background-color: #9FB4D9; //afeeee;
font-family: monospace;
font-size: 18px;
}
.main-panel-content {
padding: 5px;
margin-bottom: 10px;
background-color: #DEE8FC; //d0ffff;
}
.rounded-window {
border-radius: 10px;
border-style: solid;
border-width: 1px;
}
.rounded-window-top {
border-top-right-radius: 10px 10px;
border-top-left-radius: 10px 10px;
border-style: solid;
border-width: 1px;
overflow: auto;
}
.light-rounded-window-top {
border-top-right-radius: 10px 10px;
border-top-left-radius: 10px 10px;
}
.rounded-window-bottom {
border-style: solid;
border-width: 0px 1px 1px 1px;
border-bottom-right-radius: 10px 10px;
border-bottom-left-radius: 10px 10px;
overflow: auto;
}
.method-name {
font-size: 12px;
font-family: monospace;
}
.method-content {
border-style: solid;
border-width: 0px 0px 1px 0px;
margin-bottom: 10;
padding-bottom: 5px;
width: 80%;
}
.parameters {
font-size: 14px;
font-family: monospace;
}
.stack-trace {
white-space: pre;
font-family: monospace;
font-size: 12px;
font-weight: bold;
margin-top: 0px;
margin-left: 20px;
}
.testng-xml {
font-family: monospace;
}
.method-list-content {
margin-left: 10px;
}
.navigator-suite-content {
margin-left: 10px;
font: 12px 'Lucida Grande';
}
.suite-section-title {
margin-top: 10px;
width: 80%;
border-style: solid;
border-width: 1px 0px 0px 0px;
font-family: Times;
font-size: 18px;
font-weight: bold;
}
.suite-section-content {
list-style-image: url(bullet_point.png);
}
.top-banner-root {
position: absolute;
top: 0;
height: 45px;
left: 0;
right: 0;
padding: 5px;
margin: 0px 0px 5px 0px;
background-color: #0066ff;
font-family: Times;
color: #fff;
text-align: center;
}
.top-banner-title-font {
font-size: 25px;
}
.test-name {
font-family: 'Lucida Grande';
font-size: 16px;
}
.suite-icon {
padding: 5px;
float: right;
height: 20;
}
.test-group {
font: 20px 'Lucida Grande';
margin: 5px 5px 10px 5px;
border-width: 0px 0px 1px 0px;
border-style: solid;
padding: 5px;
}
.test-group-name {
font-weight: bold;
}
.method-in-group {
font-size: 16px;
margin-left: 80px;
}
table.google-visualization-table-table {
width: 100%;
}
.reporter-method-name {
font-size: 14px;
font-family: monospace;
}
.reporter-method-output-div {
padding: 5px;
margin: 0px 0px 5px 20px;
font-size: 12px;
font-family: monospace;
border-width: 0px 0px 0px 1px;
border-style: solid;
}
.ignored-class-div {
font-size: 14px;
font-family: monospace;
}
.ignored-methods-div {
padding: 5px;
margin: 0px 0px 5px 20px;
font-size: 12px;
font-family: monospace;
border-width: 0px 0px 0px 1px;
border-style: solid;
}
.border-failed {
border-top-left-radius: 10px 10px;
border-bottom-left-radius: 10px 10px;
border-style: solid;
border-width: 0px 0px 0px 10px;
border-color: #f00;
}
.border-skipped {
border-top-left-radius: 10px 10px;
border-bottom-left-radius: 10px 10px;
border-style: solid;
border-width: 0px 0px 0px 10px;
border-color: #edc600;
}
.border-passed {
border-top-left-radius: 10px 10px;
border-bottom-left-radius: 10px 10px;
border-style: solid;
border-width: 0px 0px 0px 10px;
border-color: #19f52d;
}
.times-div {
text-align: center;
padding: 5px;
}
.suite-total-time {
font: 16px 'Lucida Grande';
}
.configuration-suite {
margin-left: 20px;
}
.configuration-test {
margin-left: 40px;
}
.configuration-class {
margin-left: 60px;
}
.configuration-method {
margin-left: 80px;
}
.test-method {
margin-left: 100px;
}
.chronological-class {
background-color: #0ccff;
border-style: solid;
border-width: 0px 0px 1px 1px;
}
.method-start {
float: right;
}
.chronological-class-name {
padding: 0px 0px 0px 5px;
color: #008;
}
.after, .before, .test-method {
font-family: monospace;
font-size: 14px;
}
.navigator-suite-header {
font-size: 22px;
margin: 0px 10px 5px 0px;
background-color: #deb887;
text-align: center;
}
.collapse-all-icon {
padding: 5px;
float: right;
}

View File

@ -0,0 +1,122 @@
$(document).ready(function() {
$('a.navigator-link').click(function() {
// Extract the panel for this link
var panel = getPanelName($(this));
// Mark this link as currently selected
$('.navigator-link').parent().removeClass('navigator-selected');
$(this).parent().addClass('navigator-selected');
showPanel(panel);
});
installMethodHandlers('failed');
installMethodHandlers('skipped');
installMethodHandlers('passed', true); // hide passed methods by default
$('a.method').click(function() {
showMethod($(this));
return false;
});
// Hide all the panels and display the first one (do this last
// to make sure the click() will invoke the listeners)
$('.panel').hide();
$('.navigator-link').first().click();
// Collapse/expand the suites
$('a.collapse-all-link').click(function() {
var contents = $('.navigator-suite-content');
if (contents.css('display') == 'none') {
contents.show();
} else {
contents.hide();
}
});
});
// The handlers that take care of showing/hiding the methods
function installMethodHandlers(name, hide) {
function getContent(t) {
return $('.method-list-content.' + name + "." + t.attr('panel-name'));
}
function getHideLink(t, name) {
var s = 'a.hide-methods.' + name + "." + t.attr('panel-name');
return $(s);
}
function getShowLink(t, name) {
return $('a.show-methods.' + name + "." + t.attr('panel-name'));
}
function getMethodPanelClassSel(element, name) {
var panelName = getPanelName(element);
var sel = '.' + panelName + "-class-" + name;
return $(sel);
}
$('a.hide-methods.' + name).click(function() {
var w = getContent($(this));
w.hide();
getHideLink($(this), name).hide();
getShowLink($(this), name).show();
getMethodPanelClassSel($(this), name).hide();
});
$('a.show-methods.' + name).click(function() {
var w = getContent($(this));
w.show();
getHideLink($(this), name).show();
getShowLink($(this), name).hide();
showPanel(getPanelName($(this)));
getMethodPanelClassSel($(this), name).show();
});
if (hide) {
$('a.hide-methods.' + name).click();
} else {
$('a.show-methods.' + name).click();
}
}
function getHashForMethod(element) {
return element.attr('hash-for-method');
}
function getPanelName(element) {
return element.attr('panel-name');
}
function showPanel(panelName) {
$('.panel').hide();
var panel = $('.panel[panel-name="' + panelName + '"]');
panel.show();
}
function showMethod(element) {
var hashTag = getHashForMethod(element);
var panelName = getPanelName(element);
showPanel(panelName);
var current = document.location.href;
var base = current.substring(0, current.indexOf('#'))
document.location.href = base + '#' + hashTag;
var newPosition = $(document).scrollTop() - 65;
$(document).scrollTop(newPosition);
}
function drawTable() {
for (var i = 0; i < suiteTableInitFunctions.length; i++) {
window[suiteTableInitFunctions[i]]();
}
for (var k in window.suiteTableData) {
var v = window.suiteTableData[k];
var div = v.tableDiv;
var data = v.tableData
var table = new google.visualization.Table(document.getElementById(div));
table.draw(data, {
showRowNumber : false
});
}
}

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<testng-results skipped="0" failed="0" total="0" passed="0">
<reporter-output>
</reporter-output>
<suite name="Default suite" duration-ms="5" started-at="2014-12-06T13:26:26Z" finished-at="2014-12-06T13:26:26Z">
<groups>
</groups>
<test name="Default test" duration-ms="5" started-at="2014-12-06T13:26:26Z" finished-at="2014-12-06T13:26:26Z">
</test> <!-- Default test -->
</suite> <!-- Default suite -->
</testng-results>

View File

@ -0,0 +1,9 @@
.invocation-failed, .test-failed { background-color: #DD0000; }
.invocation-percent, .test-percent { background-color: #006600; }
.invocation-passed, .test-passed { background-color: #00AA00; }
.invocation-skipped, .test-skipped { background-color: #CCCC00; }
.main-page {
font-size: x-large;
}

View File

@ -32,7 +32,7 @@ public interface Cryptor extends SensitiveDataSwipeListener {
* @return Encrypted path components concatenated by the given encryptedPathSep. Must not start with encryptedPathSep, unless the
* encrypted path is explicitly absolute.
*/
String encryptPath(String cleartextPath, char encryptedPathSep, char cleartextPathSep);
String encryptPath(String cleartextPath, char encryptedPathSep, char cleartextPathSep, CryptorIOSupport ioSupport);
/**
* Decrypts each encrypted path component for its own.
@ -46,7 +46,7 @@ public interface Cryptor extends SensitiveDataSwipeListener {
* @return Decrypted path components concatenated by the given cleartextPathSep. Must not start with cleartextPathSep, unless the
* cleartext path is explicitly absolute.
*/
String decryptPath(String encryptedPath, char encryptedPathSep, char cleartextPathSep);
String decryptPath(String encryptedPath, char encryptedPathSep, char cleartextPathSep, CryptorIOSupport ioSupport);
/**
* @param metadataSupport Support object allowing the Cryptor to read and write its own metadata to the location of the encrypted file.

View File

@ -0,0 +1,23 @@
package org.cryptomator.crypto;
import java.io.IOException;
/**
* Methods that may be called by the Cryptor when accessing a path.
*/
public interface CryptorIOSupport {
/**
* Persists encryptedMetadata to the given encryptedPath.
*
* @param encryptedPath A relative path
* @throws IOException
*/
void writePathSpecificMetadata(String encryptedPath, byte[] encryptedMetadata) throws IOException;
/**
* @return Previously written encryptedMetadata stored at the given encryptedPath.
*/
byte[] readPathSpecificMetadata(String encryptedPath) throws IOException;
}

View File

@ -67,6 +67,7 @@ public class InitializeController implements Initializable {
OutputStream masterKeyOutputStream = null;
try {
masterKeyOutputStream = Files.newOutputStream(masterKeyPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
cryptor.randomizeMasterKey();
cryptor.encryptMasterKey(masterKeyOutputStream, password);
cryptor.swipeSensitiveData();
if (callback != null) {

View File

@ -27,7 +27,7 @@ public final class WebDavMounter {
private WebDavMounter() {
throw new IllegalStateException("not instantiable.");
}
/**
* @return Unmount Command
*/
@ -58,17 +58,18 @@ public final class WebDavMounter {
try {
final Process proc;
if (SystemUtils.IS_OS_WINDOWS) {
proc = Runtime.getRuntime().exec(new String[]{"cmd", "/C", cmd});
proc = Runtime.getRuntime().exec(new String[] {"cmd", "/C", cmd});
} else {
proc = Runtime.getRuntime().exec(new String[] {"/bin/sh", "-c", cmd});
}
if (proc.waitFor(timoutSeconds, TimeUnit.SECONDS)) {
if (!proc.waitFor(timoutSeconds, TimeUnit.SECONDS)) {
proc.destroy();
throw new CommandFailedException("Timeout executing command " + cmd);
}
if (proc.exitValue() != 0) {
throw new CommandFailedException(IOUtils.toString(proc.getErrorStream()));
}
return IOUtils.toString(proc.getInputStream());
return IOUtils.toString(proc.getInputStream());
} catch (IOException | InterruptedException | IllegalThreadStateException e) {
LOG.error("Command execution failed.", e);
throw new CommandFailedException(e);