mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-19 19:21:27 +00:00
This commit is contained in:
@@ -23,13 +23,15 @@ public interface FilenameCryptor {
|
||||
|
||||
/**
|
||||
* @param cleartextName original filename including cleartext file extension
|
||||
* @param associatedData optional associated data, that will not get encrypted but needs to be provided during decryption
|
||||
* @return encrypted filename without any file extension
|
||||
*/
|
||||
String encryptFilename(String cleartextName);
|
||||
String encryptFilename(String cleartextName, byte[]... associatedData);
|
||||
|
||||
/**
|
||||
* @param ciphertextName Ciphertext only, with any additional strings like file extensions stripped first.
|
||||
* @param associatedData the same associated data used during encryption, otherwise and {@link AuthenticationFailedException} will be thrown
|
||||
* @return cleartext filename, probably including its cleartext file extension.
|
||||
*/
|
||||
String decryptFilename(String ciphertextName) throws AuthenticationFailedException;
|
||||
String decryptFilename(String ciphertextName, byte[]... associatedData) throws AuthenticationFailedException;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.engine.impl;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
@@ -37,25 +38,25 @@ class FilenameCryptorImpl implements FilenameCryptor {
|
||||
|
||||
@Override
|
||||
public String hashDirectoryId(String cleartextDirectoryId) {
|
||||
final byte[] cleartextBytes = cleartextDirectoryId.getBytes(StandardCharsets.UTF_8);
|
||||
final byte[] cleartextBytes = cleartextDirectoryId.getBytes(UTF_8);
|
||||
byte[] encryptedBytes = AES_SIV.encrypt(encryptionKey, macKey, cleartextBytes);
|
||||
final byte[] hashedBytes = SHA1.get().digest(encryptedBytes);
|
||||
return BASE32.encodeAsString(hashedBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encryptFilename(String cleartextName) {
|
||||
final byte[] cleartextBytes = cleartextName.getBytes(StandardCharsets.UTF_8);
|
||||
final byte[] encryptedBytes = AES_SIV.encrypt(encryptionKey, macKey, cleartextBytes);
|
||||
public String encryptFilename(String cleartextName, byte[]... associatedData) {
|
||||
final byte[] cleartextBytes = cleartextName.getBytes(UTF_8);
|
||||
final byte[] encryptedBytes = AES_SIV.encrypt(encryptionKey, macKey, cleartextBytes, associatedData);
|
||||
return BASE32.encodeAsString(encryptedBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decryptFilename(String ciphertextName) throws AuthenticationFailedException {
|
||||
public String decryptFilename(String ciphertextName, byte[]... associatedData) throws AuthenticationFailedException {
|
||||
final byte[] encryptedBytes = BASE32.decode(ciphertextName);
|
||||
try {
|
||||
final byte[] cleartextBytes = AES_SIV.decrypt(encryptionKey, macKey, encryptedBytes);
|
||||
return new String(cleartextBytes, StandardCharsets.UTF_8);
|
||||
final byte[] cleartextBytes = AES_SIV.decrypt(encryptionKey, macKey, encryptedBytes, associatedData);
|
||||
return new String(cleartextBytes, UTF_8);
|
||||
} catch (AEADBadTagException e) {
|
||||
throw new AuthenticationFailedException("Authentication failed.", e);
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.engine.impl;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.bouncycastle.crypto.generators.SCrypt;
|
||||
@@ -34,7 +35,7 @@ final class Scrypt {
|
||||
*/
|
||||
public static byte[] scrypt(CharSequence passphrase, byte[] salt, int costParam, int blockSize, int keyLengthInBytes) {
|
||||
// This is an attempt to get the password bytes without copies of the password being created in some dark places inside the JVM:
|
||||
final ByteBuffer buf = StandardCharsets.UTF_8.encode(CharBuffer.wrap(passphrase));
|
||||
final ByteBuffer buf = UTF_8.encode(CharBuffer.wrap(passphrase));
|
||||
final byte[] pw = new byte[buf.remaining()];
|
||||
buf.get(pw);
|
||||
try {
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.filesystem.crypto;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
@@ -52,7 +53,7 @@ class CryptoFolder extends CryptoNode implements Folder {
|
||||
if (directoryId.get() == null) {
|
||||
File dirFile = physicalFile();
|
||||
if (dirFile.exists()) {
|
||||
try (Reader reader = Channels.newReader(dirFile.openReadable(), StandardCharsets.UTF_8.newDecoder(), -1)) {
|
||||
try (Reader reader = Channels.newReader(dirFile.openReadable(), UTF_8.newDecoder(), -1)) {
|
||||
directoryId.set(IOUtils.toString(reader));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.engine;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
@@ -22,18 +23,18 @@ class NoFilenameCryptor implements FilenameCryptor {
|
||||
|
||||
@Override
|
||||
public String hashDirectoryId(String cleartextDirectoryId) {
|
||||
final byte[] cleartextBytes = cleartextDirectoryId.getBytes(StandardCharsets.UTF_8);
|
||||
final byte[] cleartextBytes = cleartextDirectoryId.getBytes(UTF_8);
|
||||
final byte[] hashedBytes = SHA1.get().digest(cleartextBytes);
|
||||
return BASE32.encodeAsString(hashedBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encryptFilename(String cleartextName) {
|
||||
public String encryptFilename(String cleartextName, byte[]... associatedData) {
|
||||
return cleartextName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decryptFilename(String ciphertextName) {
|
||||
public String decryptFilename(String ciphertextName, byte[]... associatedData) {
|
||||
return ciphertextName;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.engine.impl;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
@@ -71,9 +72,32 @@ public class FilenameCryptorImplTest {
|
||||
final SecretKey macKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final FilenameCryptor filenameCryptor = new FilenameCryptorImpl(encryptionKey, macKey);
|
||||
|
||||
final byte[] encrypted = filenameCryptor.encryptFilename("test").getBytes(StandardCharsets.UTF_8);
|
||||
final byte[] encrypted = filenameCryptor.encryptFilename("test").getBytes(UTF_8);
|
||||
encrypted[0] ^= (byte) 0x01; // change 1 bit in first byte
|
||||
filenameCryptor.decryptFilename(new String(encrypted, StandardCharsets.UTF_8));
|
||||
filenameCryptor.decryptFilename(new String(encrypted, UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeterministicEncryptionOfFilenamesWithAssociatedData() {
|
||||
final byte[] keyBytes = new byte[32];
|
||||
final SecretKey encryptionKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final SecretKey macKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final FilenameCryptor filenameCryptor = new FilenameCryptorImpl(encryptionKey, macKey);
|
||||
|
||||
final String encrypted = filenameCryptor.encryptFilename("test", "ad".getBytes(UTF_8));
|
||||
final String decrypted = filenameCryptor.decryptFilename(encrypted, "ad".getBytes(UTF_8));
|
||||
Assert.assertEquals("test", decrypted);
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationFailedException.class)
|
||||
public void testDeterministicEncryptionOfFilenamesWithWrongAssociatedData() {
|
||||
final byte[] keyBytes = new byte[32];
|
||||
final SecretKey encryptionKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final SecretKey macKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final FilenameCryptor filenameCryptor = new FilenameCryptorImpl(encryptionKey, macKey);
|
||||
|
||||
final String encrypted = filenameCryptor.encryptFilename("test", "right".getBytes(UTF_8));
|
||||
filenameCryptor.decryptFilename(encrypted, "wrong".getBytes(UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package org.cryptomator.filesystem.shortening;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
@@ -55,7 +56,7 @@ class FilenameShortener {
|
||||
final File mappingFile = mappingFile(shortName);
|
||||
if (!mappingFile.exists()) {
|
||||
mappingFile.parent().get().create();
|
||||
try (Writer writer = Channels.newWriter(mappingFile.openWritable(), StandardCharsets.UTF_8.newEncoder(), -1)) {
|
||||
try (Writer writer = Channels.newWriter(mappingFile.openWritable(), UTF_8.newEncoder(), -1)) {
|
||||
writer.write(longName);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
@@ -73,7 +74,7 @@ class FilenameShortener {
|
||||
if (!mappingFile.exists()) {
|
||||
throw new UncheckedIOException(new FileNotFoundException("Mapping file not found " + mappingFile));
|
||||
} else {
|
||||
try (Reader reader = Channels.newReader(mappingFile.openReadable(), StandardCharsets.UTF_8.newDecoder(), -1)) {
|
||||
try (Reader reader = Channels.newReader(mappingFile.openReadable(), UTF_8.newDecoder(), -1)) {
|
||||
return IOUtils.toString(reader);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
|
||||
Reference in New Issue
Block a user