This commit is contained in:
Sebastian Stenzel
2014-12-11 20:07:56 +01:00
54 changed files with 391 additions and 887 deletions

1
.gitignore vendored
View File

@@ -10,3 +10,4 @@
.project
.classpath
target/
test-output/

View File

@@ -3,7 +3,7 @@ Cryptomator
Multiplatform transparent client-side encryption of your files in the cloud. You need Java 8 in order to run the application. Get the runtime environment here: http://www.oracle.com/technetwork/java/javase/downloads/index.html
If you run OS X and want to take a look at the current alpha version, go ahead and [download Cryptomator.dmg](https://github.com/totalvoidness/cryptomator/releases/download/v0.1.0/Cryptomator.dmg).
If you want to take a look at the current beta version, go ahead and download [Cryptomator.dmg](https://github.com/totalvoidness/cryptomator/releases/download/v0.2.0/Cryptomator.dmg), [Cryptomator.exe](https://github.com/totalvoidness/cryptomator/releases/download/v0.2.0/Cryptomator.exe) or [Cryptomator.jar](https://github.com/totalvoidness/cryptomator/releases/download/v0.2.0/Cryptomator.jar).
## Features
- Totally transparent: Just work on the encrypted volume, as if it was an USB drive
@@ -30,7 +30,7 @@ If you run OS X and want to take a look at the current alpha version, go ahead a
- *NEW:* No Metadata at all. Encrypted files can be decrypted even on completely shuffled file systems (if their contents are undamaged).
## Dependencies
- Java 8 (for UI only - runs headless on Java 7)
- Java 8
- Maven
- Awesome 3rd party open source libraries (Apache Commons, Apache Jackrabbit, Jetty, Jackson, ...)

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.2.0</version>
<version>0.3.0-SNAPSHOT</version>
</parent>
<artifactId>core</artifactId>
<name>Cryptomator core I/O module</name>
@@ -63,18 +63,4 @@
<artifactId>commons-collections4</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -139,8 +139,8 @@ public class EncryptedDir extends AbstractEncryptedNode {
if (Files.exists(path)) {
try {
final BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
properties.add(new DefaultDavProperty<Long>(DavPropertyName.CREATIONDATE, attrs.creationTime().toMillis()));
properties.add(new DefaultDavProperty<Long>(DavPropertyName.GETLASTMODIFIED, attrs.lastModifiedTime().toMillis()));
properties.add(new DefaultDavProperty<String>(DavPropertyName.CREATIONDATE, FileTimeUtils.toRfc1123String(attrs.creationTime())));
properties.add(new DefaultDavProperty<String>(DavPropertyName.GETLASTMODIFIED, FileTimeUtils.toRfc1123String(attrs.lastModifiedTime())));
} catch (IOException e) {
LOG.error("Error determining metadata " + path.toString(), e);
// don't add any further properties

View File

@@ -96,8 +96,8 @@ public class EncryptedFile extends AbstractEncryptedNode {
properties.add(new DefaultDavProperty<Long>(DavPropertyName.GETCONTENTLENGTH, contentLength));
final BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
properties.add(new DefaultDavProperty<Long>(DavPropertyName.CREATIONDATE, attrs.creationTime().toMillis()));
properties.add(new DefaultDavProperty<Long>(DavPropertyName.GETLASTMODIFIED, attrs.lastModifiedTime().toMillis()));
properties.add(new DefaultDavProperty<String>(DavPropertyName.CREATIONDATE, FileTimeUtils.toRfc1123String(attrs.creationTime())));
properties.add(new DefaultDavProperty<String>(DavPropertyName.GETLASTMODIFIED, FileTimeUtils.toRfc1123String(attrs.lastModifiedTime())));
} catch (IOException e) {
LOG.error("Error determining metadata " + path.toString(), e);
throw new IORuntimeException(e);

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.webdav.jackrabbit.resources;
import java.nio.file.attribute.FileTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.Temporal;
public final class FileTimeUtils {
private FileTimeUtils() {
throw new IllegalStateException("not instantiable");
}
public static String toRfc1123String(FileTime time) {
final Temporal date = OffsetDateTime.ofInstant(time.toInstant(), ZoneOffset.UTC);
return DateTimeFormatter.RFC_1123_DATE_TIME.format(date);
}
}

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.2.0</version>
<version>0.3.0-SNAPSHOT</version>
</parent>
<artifactId>crypto-aes</artifactId>
<name>Cryptomator cryptographic module (AES)</name>
@@ -48,18 +48,4 @@
<artifactId>jackson-databind</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -42,7 +42,6 @@ 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;
@@ -78,11 +77,6 @@ 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.
*/
@@ -92,7 +86,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 = Arrays.copyOf(EMPTY_MASTER_KEY, MASTER_KEY_LENGTH);
private final byte[] masterKey = new byte[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;
@@ -108,10 +102,7 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
}
}
/**
* Fills the masterkey with new random bytes.
*/
public void randomizeMasterKey() {
public Aes256Cryptor() {
SECURE_PRNG.setSeed(SECURE_PRNG.generateSeed(PRNG_SEED_LENGTH));
SECURE_PRNG.nextBytes(this.masterKey);
}
@@ -119,10 +110,8 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
/**
* Encrypts the current masterKey with the given password and writes the result to the given output stream.
*/
@Override
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);
@@ -157,6 +146,7 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
* @throws UnsupportedKeyLengthException If the masterkey has been encrypted with a higher key length than supported by the system. In
* this case Java JCE needs to be installed.
*/
@Override
public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException {
byte[] decrypted = new byte[0];
try {

View File

@@ -54,20 +54,11 @@ 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();
@@ -81,7 +72,6 @@ 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();
@@ -96,7 +86,6 @@ 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();
@@ -111,7 +100,6 @@ 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();
@@ -124,7 +112,6 @@ public class Aes256CryptorTest {
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";

View File

@@ -1,71 +0,0 @@
<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>Mon Dec 08 22:07:11 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

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 B

View File

@@ -1,2 +0,0 @@
<!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.

Before

Width:  |  Height:  |  Size: 977 B

View File

@@ -1,191 +0,0 @@
<!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--34592626/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;/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.

Before

Width:  |  Height:  |  Size: 352 B

View File

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

View File

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

View File

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

View File

@@ -1,6 +0,0 @@
<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

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

View File

@@ -1,2 +0,0 @@
<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

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

View File

@@ -1,2 +0,0 @@
<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

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

View File

@@ -1 +0,0 @@
<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;&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

@@ -1,30 +0,0 @@
<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

@@ -1,9 +0,0 @@
<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.

Before

Width:  |  Height:  |  Size: 1019 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 967 B

View File

@@ -1,309 +0,0 @@
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

@@ -1,122 +0,0 @@
$(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

@@ -1,11 +0,0 @@
<?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-08T21:07:11Z" finished-at="2014-12-08T21:07:11Z">
<groups>
</groups>
<test name="Default test" duration-ms="5" started-at="2014-12-08T21:07:11Z" finished-at="2014-12-08T21:07:11Z">
</test> <!-- Default test -->
</suite> <!-- Default suite -->
</testng-results>

View File

@@ -1,9 +0,0 @@
.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

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.2.0</version>
<version>0.3.0-SNAPSHOT</version>
</parent>
<artifactId>crypto-api</artifactId>
<name>Cryptomator cryptographic module API</name>
@@ -22,19 +22,9 @@
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,3 +1,11 @@
/*******************************************************************************
* 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;
import java.util.HashSet;

View File

@@ -15,11 +15,31 @@ import java.nio.channels.SeekableByteChannel;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.Path;
import org.cryptomator.crypto.exceptions.DecryptFailedException;
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
import org.cryptomator.crypto.exceptions.WrongPasswordException;
/**
* Provides access to cryptographic functions. All methods are threadsafe.
*/
public interface Cryptor extends SensitiveDataSwipeListener {
/**
* Encrypts the current masterKey with the given password and writes the result to the given output stream.
*/
void encryptMasterKey(OutputStream out, CharSequence password) throws IOException;
/**
* Reads the encrypted masterkey from the given input stream and decrypts it with the given password.
*
* @throws DecryptFailedException If the decryption failed for various reasons (including wrong password).
* @throws WrongPasswordException If the provided password was wrong. Note: Sometimes the algorithm itself fails due to a wrong
* password. In this case a DecryptFailedException will be thrown.
* @throws UnsupportedKeyLengthException If the masterkey has been encrypted with a higher key length than supported by the system. In
* this case Java JCE needs to be installed.
*/
void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException;
/**
* Encrypts each plaintext path component for its own.
*

View File

@@ -0,0 +1,26 @@
/*******************************************************************************
* 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;
/**
* Optional monitoring interface. If a cryptor implements this interface, it counts bytes de- and encrypted in a thread-safe manner.
*/
public interface CryptorIOSampling {
/**
* @return Number of encrypted bytes since the last reset.
*/
Long pollEncryptedBytes(boolean resetCounter);
/**
* @return Number of decrypted bytes since the last reset.
*/
Long pollDecryptedBytes(boolean resetCounter);
}

View File

@@ -1,3 +1,11 @@
/*******************************************************************************
* 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;
import java.io.IOException;

View File

@@ -0,0 +1,161 @@
package org.cryptomator.crypto;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.exceptions.DecryptFailedException;
import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
import org.cryptomator.crypto.exceptions.WrongPasswordException;
public class SamplingDecorator implements Cryptor, CryptorIOSampling {
private final Cryptor cryptor;
private final AtomicLong encryptedBytes;
private final AtomicLong decryptedBytes;
private SamplingDecorator(Cryptor cryptor) {
this.cryptor = cryptor;
encryptedBytes = new AtomicLong();
decryptedBytes = new AtomicLong();
}
public static Cryptor decorate(Cryptor cryptor) {
return new SamplingDecorator(cryptor);
}
@Override
public void swipeSensitiveData() {
cryptor.swipeSensitiveData();
}
@Override
public Long pollEncryptedBytes(boolean resetCounter) {
if (resetCounter) {
return encryptedBytes.getAndSet(0);
} else {
return encryptedBytes.get();
}
}
@Override
public Long pollDecryptedBytes(boolean resetCounter) {
if (resetCounter) {
return decryptedBytes.getAndSet(0);
} else {
return decryptedBytes.get();
}
}
/* Cryptor */
@Override
public void encryptMasterKey(OutputStream out, CharSequence password) throws IOException {
cryptor.encryptMasterKey(out, password);
}
@Override
public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException {
cryptor.decryptMasterKey(in, password);
}
@Override
public String encryptPath(String cleartextPath, char encryptedPathSep, char cleartextPathSep, CryptorIOSupport ioSupport) {
encryptedBytes.addAndGet(StringUtils.length(cleartextPath));
return cryptor.encryptPath(cleartextPath, encryptedPathSep, cleartextPathSep, ioSupport);
}
@Override
public String decryptPath(String encryptedPath, char encryptedPathSep, char cleartextPathSep, CryptorIOSupport ioSupport) {
decryptedBytes.addAndGet(StringUtils.length(encryptedPath));
return cryptor.decryptPath(encryptedPath, encryptedPathSep, cleartextPathSep, ioSupport);
}
@Override
public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException {
return cryptor.decryptedContentLength(encryptedFile);
}
@Override
public Long decryptedFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile) throws IOException {
final OutputStream countingInputStream = new CountingOutputStream(decryptedBytes, plaintextFile);
return cryptor.decryptedFile(encryptedFile, countingInputStream);
}
@Override
public Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException {
final InputStream countingInputStream = new CountingInputStream(encryptedBytes, plaintextFile);
return cryptor.encryptFile(countingInputStream, encryptedFile);
}
@Override
public Filter<Path> getPayloadFilesFilter() {
return cryptor.getPayloadFilesFilter();
}
@Override
public void addSensitiveDataSwipeListener(SensitiveDataSwipeListener listener) {
cryptor.addSensitiveDataSwipeListener(listener);
}
@Override
public void removeSensitiveDataSwipeListener(SensitiveDataSwipeListener listener) {
cryptor.removeSensitiveDataSwipeListener(listener);
}
private class CountingInputStream extends InputStream {
private final InputStream in;
private final AtomicLong counter;
private CountingInputStream(AtomicLong counter, InputStream in) {
this.in = in;
this.counter = counter;
}
@Override
public int read() throws IOException {
int count = in.read();
counter.addAndGet(count);
return count;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int count = in.read(b, off, len);
counter.addAndGet(count);
return count;
}
}
private class CountingOutputStream extends OutputStream {
private final OutputStream out;
private final AtomicLong counter;
private CountingOutputStream(AtomicLong counter, OutputStream out) {
this.out = out;
this.counter = counter;
}
@Override
public void write(int b) throws IOException {
counter.incrementAndGet();
out.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
counter.addAndGet(len);
out.write(b, off, len);
}
}
}

View File

@@ -1,3 +1,11 @@
/*******************************************************************************
* 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;
public interface SensitiveDataSwipeListener {

View File

@@ -7,7 +7,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.2.0</version>
<version>0.3.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Cryptomator</name>
@@ -141,5 +141,19 @@
<module>core</module>
<module>ui</module>
</modules>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -12,7 +12,7 @@
<parent>
<groupId>org.cryptomator</groupId>
<artifactId>main</artifactId>
<version>0.2.0</version>
<version>0.3.0-SNAPSHOT</version>
</parent>
<artifactId>ui</artifactId>
<name>Cryptomator GUI</name>
@@ -60,16 +60,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>

View File

@@ -132,7 +132,6 @@ public class InitializeController implements Initializable {
OutputStream masterKeyOutputStream = null;
try {
masterKeyOutputStream = Files.newOutputStream(masterKeyPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
directory.getCryptor().randomizeMasterKey();
directory.getCryptor().encryptMasterKey(masterKeyOutputStream, password);
encryptExistingContents();
directory.getCryptor().swipeSensitiveData();
@@ -152,7 +151,7 @@ public class InitializeController implements Initializable {
IOUtils.closeQuietly(masterKeyOutputStream);
}
}
private boolean isDirectoryEmpty() {
try {
final DirectoryStream<Path> dirContents = Files.newDirectoryStream(directory.getPath());
@@ -162,22 +161,22 @@ public class InitializeController implements Initializable {
throw new IllegalStateException(e);
}
}
private boolean shouldEncryptExistingFiles() {
final Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle(localization.getString("initialize.alert.directoryIsNotEmpty.title"));
alert.setHeaderText(localization.getString("initialize.alert.directoryIsNotEmpty.header"));
alert.setHeaderText(null);
alert.setContentText(localization.getString("initialize.alert.directoryIsNotEmpty.content"));
final Optional<ButtonType> result = alert.showAndWait();
return ButtonType.OK.equals(result.get());
}
private void encryptExistingContents() throws IOException {
final FileVisitor<Path> visitor = new EncryptingFileVisitor(directory.getPath(), directory.getCryptor(), this::shouldEncryptExistingFile);
Files.walkFileTree(directory.getPath(), visitor);
}
private boolean shouldEncryptExistingFile(Path path) {
final String name = path.getFileName().toString();
return !directory.getPath().equals(path) && !name.endsWith(Aes256Cryptor.BASIC_FILE_EXT) && !name.endsWith(Aes256Cryptor.METADATA_FILE_EXT) && !name.endsWith(Aes256Cryptor.MASTERKEY_FILE_EXT);

View File

@@ -11,26 +11,44 @@ package org.cryptomator.ui;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Label;
import javafx.util.Duration;
import org.cryptomator.crypto.CryptorIOSampling;
import org.cryptomator.ui.model.Directory;
public class UnlockedController implements Initializable {
private static final int IO_SAMPLING_STEPS = 100;
private static final double IO_SAMPLING_INTERVAL = 0.25;
private ResourceBundle rb;
private LockListener listener;
private Directory directory;
private Timeline ioAnimation;
@FXML
private Label messageLabel;
@FXML
private LineChart<Number, Number> ioGraph;
@FXML
private NumberAxis xAxis;
@Override
public void initialize(URL url, ResourceBundle rb) {
this.rb = rb;
}
@FXML
@@ -43,6 +61,60 @@ public class UnlockedController implements Initializable {
}
}
// ****************************************
// IO Graph
// ****************************************
private void startIoSampling(final CryptorIOSampling sampler) {
final Series<Number, Number> decryptedBytes = new Series<>();
decryptedBytes.setName("decrypted");
final Series<Number, Number> encryptedBytes = new Series<>();
encryptedBytes.setName("encrypted");
ioGraph.getData().add(decryptedBytes);
ioGraph.getData().add(encryptedBytes);
ioAnimation = new Timeline();
ioAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(IO_SAMPLING_INTERVAL), new IoSamplingAnimationHandler(sampler, decryptedBytes, encryptedBytes)));
ioAnimation.setCycleCount(Animation.INDEFINITE);
ioAnimation.play();
}
private class IoSamplingAnimationHandler implements EventHandler<ActionEvent> {
private static final double BYTES_TO_MEGABYTES_FACTOR = IO_SAMPLING_INTERVAL / 1024.0 / 1024.0;
private final CryptorIOSampling sampler;
private final Series<Number, Number> decryptedBytes;
private final Series<Number, Number> encryptedBytes;
private int step = 0;
public IoSamplingAnimationHandler(CryptorIOSampling sampler, Series<Number, Number> decryptedBytes, Series<Number, Number> encryptedBytes) {
this.sampler = sampler;
this.decryptedBytes = decryptedBytes;
this.encryptedBytes = encryptedBytes;
}
@Override
public void handle(ActionEvent event) {
step++;
final double decryptedMb = sampler.pollDecryptedBytes(true) * BYTES_TO_MEGABYTES_FACTOR;
decryptedBytes.getData().add(new Data<Number, Number>(step, decryptedMb));
if (decryptedBytes.getData().size() > IO_SAMPLING_STEPS) {
decryptedBytes.getData().remove(0);
}
final double encrypteddMb = sampler.pollEncryptedBytes(true) * BYTES_TO_MEGABYTES_FACTOR;
encryptedBytes.getData().add(new Data<Number, Number>(step, encrypteddMb));
if (encryptedBytes.getData().size() > IO_SAMPLING_STEPS) {
encryptedBytes.getData().remove(0);
}
xAxis.setLowerBound(step - IO_SAMPLING_STEPS);
xAxis.setUpperBound(step);
}
}
/* Getter/Setter */
public Directory getDirectory() {
@@ -53,6 +125,12 @@ public class UnlockedController implements Initializable {
this.directory = directory;
final String msg = String.format(rb.getString("unlocked.messageLabel.runningOnPort"), directory.getServer().getPort());
messageLabel.setText(msg);
if (directory.getCryptor() instanceof CryptorIOSampling) {
startIoSampling((CryptorIOSampling) directory.getCryptor());
} else {
ioGraph.setVisible(false);
}
}
public LockListener getListener() {

View File

@@ -6,6 +6,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.crypto.SamplingDecorator;
import org.cryptomator.crypto.aes256.Aes256Cryptor;
import org.cryptomator.ui.MainApplication;
import org.cryptomator.ui.util.MasterKeyFilter;
@@ -26,7 +28,7 @@ public class Directory implements Serializable {
private static final Logger LOG = LoggerFactory.getLogger(Directory.class);
private final WebDAVServer server = new WebDAVServer();
private final Aes256Cryptor cryptor = new Aes256Cryptor();
private final Cryptor cryptor = SamplingDecorator.decorate(new Aes256Cryptor());
private final Path path;
private boolean unlocked;
private String unmountCommand;
@@ -97,7 +99,7 @@ public class Directory implements Serializable {
return path.getFileName().toString();
}
public Aes256Cryptor getCryptor() {
public Cryptor getCryptor() {
return cryptor;
}

View File

@@ -19,8 +19,7 @@ initialize.label.username=Username
initialize.label.password=Password
initialize.label.retypePassword=Retype password
initialize.button.ok=Create vault
initialize.alert.directoryIsNotEmpty.title=Confirm
initialize.alert.directoryIsNotEmpty.header=The chosen directory is not empty.
initialize.alert.directoryIsNotEmpty.title=The chosen directory is not empty
initialize.alert.directoryIsNotEmpty.content=All existing files inside this directory will get encrypted. Continue?
@@ -37,6 +36,7 @@ unlock.messageLabel.startServerFailed=Starting WebDAV server failed.
# unlocked.fxml
unlocked.messageLabel.runningOnPort=Vault is accessible via WebDAV on local port %d.
unlocked.button.lock=Lock vault
unlocked.ioGraph.yAxis.label=Throughput (MiB/s)
# tray icon

View File

@@ -14,6 +14,8 @@
<?import javafx.scene.text.*?>
<?import java.lang.String?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<GridPane vgap="12.0" hgap="12.0" prefWidth="400.0" fx:controller="org.cryptomator.ui.UnlockedController" xmlns:fx="http://javafx.com/fxml">
@@ -32,6 +34,12 @@
<!-- Row 1 -->
<Button text="%unlocked.button.lock" defaultButton="true" GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" prefWidth="150.0" onAction="#closeVault" focusTraversable="false"/>
<!-- Row 2 -->
<LineChart fx:id="ioGraph" GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" animated="false" createSymbols="false" prefHeight="300.0" legendVisible="true" legendSide="BOTTOM" verticalZeroLineVisible="false" verticalGridLinesVisible="false" horizontalGridLinesVisible="true">
<xAxis><NumberAxis fx:id="xAxis" forceZeroInRange="false" tickMarkVisible="false" minorTickVisible="false" tickLabelsVisible="false" autoRanging="false"/></xAxis>
<yAxis><NumberAxis label="%unlocked.ioGraph.yAxis.label" autoRanging="true" forceZeroInRange="true" /></yAxis>
</LineChart>
</children>
</GridPane>