mirror of
https://github.com/cryptomator/cryptomator.git
synced 2024-11-23 12:09:45 +00:00
- 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:
parent
8ae7e95c41
commit
ce197b3314
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
71
main/crypto-aes/test-output/Default suite/Default test.html
Normal file
71
main/crypto-aes/test-output/Default suite/Default test.html
Normal 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>
|
@ -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>
|
BIN
main/crypto-aes/test-output/bullet_point.png
Normal file
BIN
main/crypto-aes/test-output/bullet_point.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 356 B |
BIN
main/crypto-aes/test-output/collapseall.gif
Normal file
BIN
main/crypto-aes/test-output/collapseall.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 157 B |
2
main/crypto-aes/test-output/emailable-report.html
Normal file
2
main/crypto-aes/test-output/emailable-report.html
Normal 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>
|
BIN
main/crypto-aes/test-output/failed.png
Normal file
BIN
main/crypto-aes/test-output/failed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 977 B |
195
main/crypto-aes/test-output/index.html
Normal file
195
main/crypto-aes/test-output/index.html
Normal 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>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
|
||||
<suite name="Default suite">
|
||||
<test verbose="2" name="Default test">
|
||||
<classes>
|
||||
<class name="org.cryptomator.crypto.aes256.Aes256CryptorTest">
|
||||
<methods>
|
||||
<include name="testEncryptionOfLongFilenames"/>
|
||||
</methods>
|
||||
</class> <!-- org.cryptomator.crypto.aes256.Aes256CryptorTest -->
|
||||
</classes>
|
||||
</test> <!-- Default test -->
|
||||
</suite> <!-- Default suite -->
|
||||
</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>
|
4
main/crypto-aes/test-output/jquery-1.7.1.min.js
vendored
Normal file
4
main/crypto-aes/test-output/jquery-1.7.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
main/crypto-aes/test-output/navigator-bullet.png
Normal file
BIN
main/crypto-aes/test-output/navigator-bullet.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 352 B |
@ -0,0 +1 @@
|
||||
[SuiteResult context=Default test]
|
@ -0,0 +1,6 @@
|
||||
<table border='1'>
|
||||
<tr>
|
||||
<th>Class name</th>
|
||||
<th>Method name</th>
|
||||
<th>Groups</th>
|
||||
</tr></table>
|
@ -0,0 +1 @@
|
||||
<h2>Groups used for this test run</h2>
|
6
main/crypto-aes/test-output/old/Default suite/index.html
Normal file
6
main/crypto-aes/test-output/old/Default suite/index.html
Normal 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>
|
2
main/crypto-aes/test-output/old/Default suite/main.html
Normal file
2
main/crypto-aes/test-output/old/Default suite/main.html
Normal 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>
|
@ -0,0 +1,2 @@
|
||||
<h2>Methods run, sorted chronologically</h2><h3>>> means before, << 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>
|
@ -0,0 +1,2 @@
|
||||
<h2>Methods that were not run</h2><table>
|
||||
</table>
|
@ -0,0 +1,2 @@
|
||||
<h2>Methods run, sorted chronologically</h2><h3>>> means before, << 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>
|
@ -0,0 +1 @@
|
||||
<h2>Reporter output</h2><table></table>
|
@ -0,0 +1 @@
|
||||
<html><head><title>testng.xml for Default suite</title></head><body><tt><?xml version="1.0" encoding="UTF-8"?><br/><!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"><br/><suite name="Default suite"><br/> <test verbose="2" name="Default test"><br/> <classes><br/> <class name="org.cryptomator.crypto.aes256.Aes256CryptorTest"><br/> <methods><br/> <include name="testEncryptionOfLongFilenames"/><br/> </methods><br/> </class> <!-- org.cryptomator.crypto.aes256.Aes256CryptorTest --><br/> </classes><br/> </test> <!-- Default test --><br/></suite> <!-- Default suite --><br/></tt></body></html>
|
30
main/crypto-aes/test-output/old/Default suite/toc.html
Normal file
30
main/crypto-aes/test-output/old/Default suite/toc.html
Normal 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/>
|
||||
<a target='mainFrame' href='methods.html'>chronological</a><br/>
|
||||
<a target='mainFrame' href='methods-alphabetical.html'>alphabetical</a><br/>
|
||||
<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>
|
9
main/crypto-aes/test-output/old/index.html
Normal file
9
main/crypto-aes/test-output/old/index.html
Normal 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> </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>
|
BIN
main/crypto-aes/test-output/passed.png
Normal file
BIN
main/crypto-aes/test-output/passed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1019 B |
BIN
main/crypto-aes/test-output/skipped.png
Normal file
BIN
main/crypto-aes/test-output/skipped.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 967 B |
309
main/crypto-aes/test-output/testng-reports.css
Normal file
309
main/crypto-aes/test-output/testng-reports.css
Normal 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;
|
||||
}
|
122
main/crypto-aes/test-output/testng-reports.js
Normal file
122
main/crypto-aes/test-output/testng-reports.js
Normal 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
|
||||
});
|
||||
}
|
||||
}
|
11
main/crypto-aes/test-output/testng-results.xml
Normal file
11
main/crypto-aes/test-output/testng-results.xml
Normal 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>
|
9
main/crypto-aes/test-output/testng.css
Normal file
9
main/crypto-aes/test-output/testng.css
Normal 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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user