mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-21 20:21:27 +00:00
cleanup
This commit is contained in:
@@ -41,6 +41,6 @@ public interface ReadableBytes {
|
||||
* if an {@link IOException} occurs while reading from this
|
||||
* {@code ReadableBytes}
|
||||
*/
|
||||
void read(ByteBuffer target, int position) throws UncheckedIOException;
|
||||
void read(ByteBuffer target, long position) throws UncheckedIOException;
|
||||
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@ package org.cryptomator.crypto.engine;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.security.auth.Destroyable;
|
||||
|
||||
public interface FileContentCryptor extends Destroyable {
|
||||
/**
|
||||
* Factory for stateful {@link FileContentEncryptor Encryptor}/{@link FileContentDecryptor Decryptor} instances, that are capable of processing data exactly once.
|
||||
*/
|
||||
public interface FileContentCryptor {
|
||||
|
||||
/**
|
||||
* @return The fixed number of bytes of the file header. The header length is implementation-specific.
|
||||
@@ -16,13 +17,13 @@ public interface FileContentCryptor extends Destroyable {
|
||||
* @param header The full fixed-length header of an encrypted file. The caller is required to pass the exact amount of bytes returned by {@link #getHeaderSize()}.
|
||||
* @return A possibly new FileContentDecryptor instance which is capable of decrypting ciphertexts associated with the given file header.
|
||||
*/
|
||||
FileContentDecryptor getFileContentDecryptor(ByteBuffer header);
|
||||
FileContentDecryptor createFileContentDecryptor(ByteBuffer header);
|
||||
|
||||
/**
|
||||
* @param header The full fixed-length header of an encrypted file or {@link Optional#empty()}. The caller is required to pass the exact amount of bytes returned by {@link #getHeaderSize()}.
|
||||
* If the header is empty, a new one will be created by the returned encryptor.
|
||||
* @return A possibly new FileContentEncryptor instance which is capable of encrypting cleartext associated with the given file header.
|
||||
*/
|
||||
FileContentEncryptor getFileContentEncryptor(Optional<ByteBuffer> header);
|
||||
FileContentEncryptor createFileContentEncryptor(Optional<ByteBuffer> header);
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* Not necessarily thread-safe.
|
||||
* Stateful, thus not thread-safe.
|
||||
*/
|
||||
public interface FileContentDecryptor {
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
/**
|
||||
* Not necessarily thread-safe.
|
||||
* Stateful, thus not thread-safe.
|
||||
*/
|
||||
public interface FileContentEncryptor {
|
||||
|
||||
|
||||
@@ -8,15 +8,13 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.engine;
|
||||
|
||||
import javax.security.auth.Destroyable;
|
||||
|
||||
/**
|
||||
* Provides deterministic encryption capabilities as filenames must not change on subsequent encryption attempts,
|
||||
* otherwise each change results in major directory structure changes which would be a terrible idea for cloud storage encryption.
|
||||
*
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Deterministic_encryption">Wikipedia on deterministic encryption</a>
|
||||
*/
|
||||
public interface FilenameCryptor extends Destroyable {
|
||||
public interface FilenameCryptor {
|
||||
|
||||
/**
|
||||
* @return constant length string, that is unlikely to collide with any other name.
|
||||
|
||||
@@ -18,6 +18,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.security.auth.DestroyFailedException;
|
||||
import javax.security.auth.Destroyable;
|
||||
|
||||
import org.cryptomator.crypto.engine.Cryptor;
|
||||
import org.cryptomator.crypto.engine.FileContentCryptor;
|
||||
@@ -52,12 +53,11 @@ public class CryptorImpl implements Cryptor {
|
||||
if (existingCryptor != null) {
|
||||
return existingCryptor;
|
||||
} else {
|
||||
final FilenameCryptorImpl newCryptor = new FilenameCryptorImpl(encryptionKey, macKey);
|
||||
final FilenameCryptor newCryptor = new FilenameCryptorImpl(encryptionKey, macKey);
|
||||
if (filenameCryptor.compareAndSet(null, newCryptor)) {
|
||||
return newCryptor;
|
||||
} else {
|
||||
// CAS failed: other thread set an object
|
||||
newCryptor.destroy();
|
||||
return filenameCryptor.get();
|
||||
}
|
||||
}
|
||||
@@ -70,12 +70,11 @@ public class CryptorImpl implements Cryptor {
|
||||
if (existingCryptor != null) {
|
||||
return existingCryptor;
|
||||
} else {
|
||||
final FileContentCryptorImpl newCryptor = new FileContentCryptorImpl(encryptionKey, macKey);
|
||||
final FileContentCryptor newCryptor = new FileContentCryptorImpl(encryptionKey, macKey);
|
||||
if (fileContentCryptor.compareAndSet(null, newCryptor)) {
|
||||
return newCryptor;
|
||||
} else {
|
||||
// CAS failed: other thread set an object
|
||||
newCryptor.destroy();
|
||||
return fileContentCryptor.get();
|
||||
}
|
||||
}
|
||||
@@ -162,22 +161,21 @@ public class CryptorImpl implements Cryptor {
|
||||
|
||||
@Override
|
||||
public void destroy() throws DestroyFailedException {
|
||||
TheDestroyer.destroyQuietly(encryptionKey);
|
||||
TheDestroyer.destroyQuietly(macKey);
|
||||
if (filenameCryptor.get() != null) {
|
||||
TheDestroyer.destroyQuietly(getFilenameCryptor());
|
||||
}
|
||||
if (fileContentCryptor.get() != null) {
|
||||
TheDestroyer.destroyQuietly(getFileContentCryptor());
|
||||
}
|
||||
destroyQuietly(encryptionKey);
|
||||
destroyQuietly(macKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return encryptionKey.isDestroyed() //
|
||||
&& macKey.isDestroyed() //
|
||||
&& (filenameCryptor.get() == null || filenameCryptor.get().isDestroyed()) //
|
||||
&& (fileContentCryptor.get() == null || fileContentCryptor.get().isDestroyed());
|
||||
return encryptionKey.isDestroyed() && macKey.isDestroyed();
|
||||
}
|
||||
|
||||
private void destroyQuietly(Destroyable d) {
|
||||
try {
|
||||
d.destroy();
|
||||
} catch (DestroyFailedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,26 +28,13 @@ class FileContentCryptorImpl implements FileContentCryptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileContentDecryptor getFileContentDecryptor(ByteBuffer header) {
|
||||
public FileContentDecryptor createFileContentDecryptor(ByteBuffer header) {
|
||||
throw new UnsupportedOperationException("Method not implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileContentEncryptor getFileContentEncryptor(Optional<ByteBuffer> header) {
|
||||
public FileContentEncryptor createFileContentEncryptor(Optional<ByteBuffer> header) {
|
||||
throw new UnsupportedOperationException("Method not implemented");
|
||||
}
|
||||
|
||||
/* ======================= destruction ======================= */
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
TheDestroyer.destroyQuietly(encryptionKey);
|
||||
TheDestroyer.destroyQuietly(macKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return encryptionKey.isDestroyed() && macKey.isDestroyed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -84,17 +84,4 @@ class FilenameCryptorImpl implements FilenameCryptor {
|
||||
}
|
||||
}
|
||||
|
||||
/* ======================= destruction ======================= */
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
TheDestroyer.destroyQuietly(encryptionKey);
|
||||
TheDestroyer.destroyQuietly(macKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return encryptionKey.isDestroyed() && macKey.isDestroyed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2015 Sebastian Stenzel and others.
|
||||
* 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.engine.impl;
|
||||
|
||||
import javax.security.auth.DestroyFailedException;
|
||||
import javax.security.auth.Destroyable;
|
||||
|
||||
final class TheDestroyer {
|
||||
|
||||
private TheDestroyer() {
|
||||
|
||||
}
|
||||
|
||||
public static void destroyQuietly(Destroyable d) {
|
||||
try {
|
||||
d.destroy();
|
||||
} catch (DestroyFailedException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,19 +15,20 @@ import org.cryptomator.io.ByteBuffers;
|
||||
class CryptoReadableFile implements ReadableFile {
|
||||
|
||||
private static final int READ_BUFFER_SIZE = 32 * 1024 + 32; // aligned with encrypted chunk size + MAC size
|
||||
private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
|
||||
|
||||
private final ExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
private final FileContentDecryptor decryptor;
|
||||
private final ReadableFile file;
|
||||
private Future<Void> readAheadTask;
|
||||
private ByteBuffer bufferedCleartext;
|
||||
private ByteBuffer bufferedCleartext = EMPTY_BUFFER;
|
||||
|
||||
public CryptoReadableFile(FileContentCryptor cryptor, ReadableFile file) {
|
||||
final int headerSize = cryptor.getHeaderSize();
|
||||
final ByteBuffer header = ByteBuffer.allocate(headerSize);
|
||||
file.read(header, 0);
|
||||
header.flip();
|
||||
this.decryptor = cryptor.getFileContentDecryptor(header);
|
||||
this.decryptor = cryptor.createFileContentDecryptor(header);
|
||||
this.file = file;
|
||||
this.prepareReadAtPhysicalPosition(headerSize + 0);
|
||||
}
|
||||
@@ -35,6 +36,7 @@ class CryptoReadableFile implements ReadableFile {
|
||||
private void prepareReadAtPhysicalPosition(long pos) {
|
||||
if (readAheadTask != null) {
|
||||
readAheadTask.cancel(true);
|
||||
bufferedCleartext = EMPTY_BUFFER;
|
||||
decryptor.cleartext().clear();
|
||||
}
|
||||
readAheadTask = executorService.submit(new Reader(pos));
|
||||
@@ -53,7 +55,7 @@ class CryptoReadableFile implements ReadableFile {
|
||||
}
|
||||
|
||||
private void bufferCleartext() throws InterruptedException {
|
||||
if (bufferedCleartext == null || !bufferedCleartext.hasRemaining()) {
|
||||
if (!bufferedCleartext.hasRemaining()) {
|
||||
bufferedCleartext = decryptor.cleartext().take();
|
||||
}
|
||||
}
|
||||
@@ -64,7 +66,7 @@ class CryptoReadableFile implements ReadableFile {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuffer target, int position) {
|
||||
public void read(ByteBuffer target, long position) {
|
||||
throw new UnsupportedOperationException("Partial read not implemented yet.");
|
||||
}
|
||||
|
||||
@@ -89,8 +91,7 @@ class CryptoReadableFile implements ReadableFile {
|
||||
|
||||
@Override
|
||||
public Void call() {
|
||||
// TODO change to file.read(ByteBuffer, long)
|
||||
file.read(ByteBuffer.allocate(0), (int) startpos);
|
||||
file.read(EMPTY_BUFFER, startpos);
|
||||
int bytesRead = -1;
|
||||
do {
|
||||
ByteBuffer ciphertext = ByteBuffer.allocate(READ_BUFFER_SIZE);
|
||||
|
||||
@@ -23,7 +23,7 @@ class CryptoWritableFile implements WritableFile {
|
||||
private final Future<Void> writeTask;
|
||||
|
||||
public CryptoWritableFile(FileContentCryptor cryptor, WritableFile file) {
|
||||
this.encryptor = cryptor.getFileContentEncryptor(Optional.empty());
|
||||
this.encryptor = cryptor.createFileContentEncryptor(Optional.empty());
|
||||
this.file = file;
|
||||
writeHeader();
|
||||
this.writeTask = executorService.submit(new Writer());
|
||||
|
||||
@@ -13,7 +13,7 @@ class NoFileContentCryptor implements FileContentCryptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileContentDecryptor getFileContentDecryptor(ByteBuffer header) {
|
||||
public FileContentDecryptor createFileContentDecryptor(ByteBuffer header) {
|
||||
if (header.remaining() != getHeaderSize()) {
|
||||
throw new IllegalArgumentException("Invalid header size.");
|
||||
}
|
||||
@@ -21,7 +21,7 @@ class NoFileContentCryptor implements FileContentCryptor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileContentEncryptor getFileContentEncryptor(Optional<ByteBuffer> header) {
|
||||
public FileContentEncryptor createFileContentEncryptor(Optional<ByteBuffer> header) {
|
||||
return new Encryptor();
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,11 @@ class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableF
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuffer target, int position) {
|
||||
content.position(position);
|
||||
public void read(ByteBuffer target, long position) {
|
||||
if (position > Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("Can not keep files of virtually unlimited size in memory.");
|
||||
}
|
||||
content.position((int) position);
|
||||
ByteBuffers.copy(content, target);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ class NioFile extends NioNode implements File {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuffer target, int position) throws UncheckedIOException {
|
||||
public void read(ByteBuffer target, long position) throws UncheckedIOException {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user