Skip to content

Commit 6bddcd2

Browse files
authored
Merge pull request #12 from chenkins/feature/uvf-draft
Update from cryptomator
2 parents 85aea94 + 2fec038 commit 6bddcd2

22 files changed

+225
-46
lines changed

src/main/java/org/cryptomator/cryptolib/api/Cryptor.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ public interface Cryptor extends Destroyable, AutoCloseable {
1616
*/
1717
FileHeaderCryptor fileHeaderCryptor();
1818

19+
/**
20+
* Encryption and decryption of file headers.
21+
* @param revision The revision of the seed to {@link RevolvingMasterkey#subKey(int, int, byte[], String) derive subkeys}.
22+
* @return utility for encrypting and decrypting file headers
23+
* @apiNote Only relevant for Universal Vault Format, for Cryptomator Vault Format see {@link #fileHeaderCryptor()}
24+
*/
25+
FileHeaderCryptor fileHeaderCryptor(int revision);
26+
1927
/**
2028
* Encryption and decryption of file names in Cryptomator Vault Format.
2129
* @return utility for encrypting and decrypting file names
@@ -35,7 +43,7 @@ public interface Cryptor extends Destroyable, AutoCloseable {
3543
* High-Level API for file name encryption and decryption
3644
* @return utility for encryption and decryption of file names in the context of a directory
3745
*/
38-
default DirectoryContentCryptor<?> directoryContentCryptor() {
46+
default DirectoryContentCryptor directoryContentCryptor() {
3947
throw new UnsupportedOperationException("not implemented");
4048
}
4149

src/main/java/org/cryptomator/cryptolib/api/DirectoryContentCryptor.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package org.cryptomator.cryptolib.api;
22

3-
public interface DirectoryContentCryptor<T extends DirectoryMetadata> {
3+
public interface DirectoryContentCryptor {
44

5-
T rootDirectoryMetadata(); // TODO required?
5+
DirectoryMetadata rootDirectoryMetadata();
66

7-
T newDirectoryMetadata();
7+
DirectoryMetadata newDirectoryMetadata();
88

99
/**
1010
* Decrypts the given directory metadata.
@@ -13,19 +13,27 @@ public interface DirectoryContentCryptor<T extends DirectoryMetadata> {
1313
* @return The decrypted directory metadata.
1414
* @throws AuthenticationFailedException If the ciphertext is unauthentic.
1515
*/
16-
T decryptDirectoryMetadata(byte[] ciphertext) throws AuthenticationFailedException;
16+
DirectoryMetadata decryptDirectoryMetadata(byte[] ciphertext) throws AuthenticationFailedException;
1717

1818
/**
1919
* Encrypts the given directory metadata.
2020
*
2121
* @param directoryMetadata The directory metadata to encrypt.
2222
* @return The encrypted directory metadata.
2323
*/
24-
byte[] encryptDirectoryMetadata(T directoryMetadata);
24+
byte[] encryptDirectoryMetadata(DirectoryMetadata directoryMetadata);
2525

26-
Decrypting fileNameDecryptor(T directoryMetadata);
26+
/**
27+
* Computes the directory path for the given directory metadata.
28+
* @param directoryMetadata The directory metadata.
29+
* @return A path relative to the vault's root (i.e. starting with `d/`).
30+
* @apiNote The path contains "/" as separator and does neither start nor end with a "/".
31+
*/
32+
String dirPath(DirectoryMetadata directoryMetadata);
33+
34+
Decrypting fileNameDecryptor(DirectoryMetadata directoryMetadata);
2735

28-
Encrypting fileNameEncryptor(T directoryMetadata);
36+
Encrypting fileNameEncryptor(DirectoryMetadata directoryMetadata);
2937

3038
@FunctionalInterface
3139
interface Decrypting {

src/main/java/org/cryptomator/cryptolib/api/Masterkey.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ static PerpetualMasterkey from(DestroyableSecretKey encKey, DestroyableSecretKey
3232
}
3333
}
3434

35+
/**
36+
* Returns the immutable directory ID of the root directory. This ID is unique for each vault and deterministically depends on the masterkey.
37+
* @return a unique but deterministic byte sequence
38+
*/
39+
byte[] rootDirId();
40+
3541
@Override
3642
void destroy();
3743

src/main/java/org/cryptomator/cryptolib/api/PerpetualMasterkey.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ public DestroyableSecretKey getMacKey() {
6868
return new DestroyableSecretKey(key, SUBKEY_LEN_BYTES, SUBKEY_LEN_BYTES, MAC_ALG);
6969
}
7070

71+
@Override
72+
public byte[] rootDirId() {
73+
// root directory ID is specified to be the "empty string":
74+
// https://docs.cryptomator.org/security/vault/#directory-ids
75+
return new byte[0];
76+
}
77+
7178
@Override
7279
public boolean isDestroyed() {
7380
return destroyed;

src/main/java/org/cryptomator/cryptolib/api/UVFMasterkey.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public int currentRevision() {
6969
return latestSeed;
7070
}
7171

72+
@Override
7273
public byte[] rootDirId() {
7374
return HKDFHelper.hkdfSha512(kdfSalt, seeds.get(initialSeed), ROOT_DIRID_KDF_CONTEXT, 32);
7475
}

src/main/java/org/cryptomator/cryptolib/v1/CryptorImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.cryptomator.cryptolib.v1;
22

33
import org.cryptomator.cryptolib.api.Cryptor;
4+
import org.cryptomator.cryptolib.api.FileHeaderCryptor;
45
import org.cryptomator.cryptolib.api.FileNameCryptor;
56
import org.cryptomator.cryptolib.api.Masterkey;
67
import org.cryptomator.cryptolib.api.PerpetualMasterkey;
@@ -37,6 +38,11 @@ public FileHeaderCryptorImpl fileHeaderCryptor() {
3738
return fileHeaderCryptor;
3839
}
3940

41+
@Override
42+
public FileHeaderCryptor fileHeaderCryptor(int revision) {
43+
throw new UnsupportedOperationException();
44+
}
45+
4046
@Override
4147
public FileNameCryptorImpl fileNameCryptor() {
4248
assertNotDestroyed();

src/main/java/org/cryptomator/cryptolib/v2/CryptorImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.cryptomator.cryptolib.v2;
22

33
import org.cryptomator.cryptolib.api.Cryptor;
4+
import org.cryptomator.cryptolib.api.FileHeaderCryptor;
45
import org.cryptomator.cryptolib.api.FileNameCryptor;
56
import org.cryptomator.cryptolib.api.Masterkey;
67
import org.cryptomator.cryptolib.api.PerpetualMasterkey;
@@ -38,6 +39,11 @@ public FileHeaderCryptorImpl fileHeaderCryptor() {
3839
return fileHeaderCryptor;
3940
}
4041

42+
@Override
43+
public FileHeaderCryptor fileHeaderCryptor(int revision) {
44+
throw new UnsupportedOperationException();
45+
}
46+
4147
@Override
4248
public FileNameCryptorImpl fileNameCryptor() {
4349
assertNotDestroyed();

src/main/java/org/cryptomator/cryptolib/v3/CryptorImpl.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import org.cryptomator.cryptolib.api.Cryptor;
1212
import org.cryptomator.cryptolib.api.DirectoryContentCryptor;
13+
import org.cryptomator.cryptolib.api.FileHeaderCryptor;
1314
import org.cryptomator.cryptolib.api.FileNameCryptor;
1415
import org.cryptomator.cryptolib.api.Masterkey;
1516
import org.cryptomator.cryptolib.api.RevolvingMasterkey;
@@ -21,7 +22,6 @@ class CryptorImpl implements Cryptor {
2122

2223
private final RevolvingMasterkey masterkey;
2324
private final FileContentCryptorImpl fileContentCryptor;
24-
private final FileHeaderCryptorImpl fileHeaderCryptor;
2525
private final SecureRandom random;
2626

2727
/**
@@ -30,7 +30,6 @@ class CryptorImpl implements Cryptor {
3030
*/
3131
CryptorImpl(RevolvingMasterkey masterkey, SecureRandom random) {
3232
this.masterkey = masterkey;
33-
this.fileHeaderCryptor = new FileHeaderCryptorImpl(masterkey, random);
3433
this.fileContentCryptor = new FileContentCryptorImpl(random);
3534
this.random = random;
3635
}
@@ -43,8 +42,13 @@ public FileContentCryptorImpl fileContentCryptor() {
4342

4443
@Override
4544
public FileHeaderCryptorImpl fileHeaderCryptor() {
45+
return fileHeaderCryptor(masterkey.currentRevision());
46+
}
47+
48+
@Override
49+
public FileHeaderCryptorImpl fileHeaderCryptor(int revision) {
4650
assertNotDestroyed();
47-
return fileHeaderCryptor;
51+
return new FileHeaderCryptorImpl(masterkey, random, revision);
4852
}
4953

5054
@Override
@@ -53,7 +57,7 @@ public FileNameCryptorImpl fileNameCryptor() {
5357
}
5458

5559
@Override
56-
public FileNameCryptor fileNameCryptor(int revision) {
60+
public FileNameCryptorImpl fileNameCryptor(int revision) {
5761
assertNotDestroyed();
5862
return new FileNameCryptorImpl(masterkey, revision);
5963
}

src/main/java/org/cryptomator/cryptolib/v3/DirectoryContentCryptorImpl.java

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.google.common.io.BaseEncoding;
44
import org.cryptomator.cryptolib.api.AuthenticationFailedException;
55
import org.cryptomator.cryptolib.api.DirectoryContentCryptor;
6+
import org.cryptomator.cryptolib.api.DirectoryMetadata;
67
import org.cryptomator.cryptolib.api.FileHeader;
78
import org.cryptomator.cryptolib.api.RevolvingMasterkey;
89

@@ -11,7 +12,7 @@
1112

1213
import static org.cryptomator.cryptolib.v3.Constants.UVF_FILE_EXT;
1314

14-
class DirectoryContentCryptorImpl implements DirectoryContentCryptor<DirectoryMetadataImpl> {
15+
class DirectoryContentCryptorImpl implements DirectoryContentCryptor {
1516

1617
private final RevolvingMasterkey masterkey;
1718
private final SecureRandom random;
@@ -27,8 +28,8 @@ public DirectoryContentCryptorImpl(RevolvingMasterkey masterkey, SecureRandom ra
2728

2829
@Override
2930
public DirectoryMetadataImpl rootDirectoryMetadata() {
30-
// TODO
31-
return null;
31+
byte[] dirId = masterkey.rootDirId();
32+
return new DirectoryMetadataImpl(masterkey.firstRevision(), dirId);
3233
}
3334

3435
@Override
@@ -58,9 +59,10 @@ public DirectoryMetadataImpl decryptDirectoryMetadata(byte[] ciphertext) throws
5859
}
5960

6061
@Override
61-
public byte[] encryptDirectoryMetadata(DirectoryMetadataImpl directoryMetadata) {
62-
ByteBuffer cleartextBuf = ByteBuffer.wrap(directoryMetadata.dirId());
63-
FileHeader header = cryptor.fileHeaderCryptor().create();
62+
public byte[] encryptDirectoryMetadata(DirectoryMetadata directoryMetadata) {
63+
DirectoryMetadataImpl metadataImpl = DirectoryMetadataImpl.cast(directoryMetadata);
64+
ByteBuffer cleartextBuf = ByteBuffer.wrap(metadataImpl.dirId());
65+
FileHeader header = cryptor.fileHeaderCryptor(metadataImpl.seedId()).create();
6466
ByteBuffer headerBuf = cryptor.fileHeaderCryptor().encryptHeader(header);
6567
ByteBuffer contentBuf = cryptor.fileContentCryptor().encryptChunk(cleartextBuf, 0, header);
6668
byte[] result = new byte[headerBuf.remaining() + contentBuf.remaining()];
@@ -69,22 +71,35 @@ public byte[] encryptDirectoryMetadata(DirectoryMetadataImpl directoryMetadata)
6971
return result;
7072
}
7173

74+
// DIR PATH
75+
76+
@Override
77+
public String dirPath(DirectoryMetadata directoryMetadata) {
78+
DirectoryMetadataImpl metadataImpl = DirectoryMetadataImpl.cast(directoryMetadata);
79+
FileNameCryptorImpl fileNameCryptor = cryptor.fileNameCryptor(metadataImpl.seedId());
80+
String dirIdStr = fileNameCryptor.hashDirectoryId(metadataImpl.dirId());
81+
assert dirIdStr.length() == 32;
82+
return "d/" + dirIdStr.substring(0, 2) + "/" + dirIdStr.substring(2);
83+
}
84+
7285
// FILE NAMES
7386

7487
@Override
75-
public Decrypting fileNameDecryptor(DirectoryMetadataImpl directoryMetadata) {
76-
byte[] dirId = directoryMetadata.dirId();
77-
FileNameCryptorImpl fileNameCryptor = new FileNameCryptorImpl(masterkey, directoryMetadata.seedId());
88+
public Decrypting fileNameDecryptor(DirectoryMetadata directoryMetadata) {
89+
DirectoryMetadataImpl metadataImpl = DirectoryMetadataImpl.cast(directoryMetadata);
90+
byte[] dirId = metadataImpl.dirId();
91+
FileNameCryptorImpl fileNameCryptor = cryptor.fileNameCryptor(metadataImpl.seedId());
7892
return ciphertextAndExt -> {
7993
String ciphertext = removeExtension(ciphertextAndExt);
8094
return fileNameCryptor.decryptFilename(BaseEncoding.base64Url(), ciphertext, dirId);
8195
};
8296
}
8397

8498
@Override
85-
public Encrypting fileNameEncryptor(DirectoryMetadataImpl directoryMetadata) {
86-
byte[] dirId = directoryMetadata.dirId();
87-
FileNameCryptorImpl fileNameCryptor = new FileNameCryptorImpl(masterkey, directoryMetadata.seedId());
99+
public Encrypting fileNameEncryptor(DirectoryMetadata directoryMetadata) {
100+
DirectoryMetadataImpl metadataImpl = DirectoryMetadataImpl.cast(directoryMetadata);
101+
byte[] dirId = metadataImpl.dirId();
102+
FileNameCryptorImpl fileNameCryptor = cryptor.fileNameCryptor(metadataImpl.seedId());
88103
return plaintext -> {
89104
String ciphertext = fileNameCryptor.encryptFilename(BaseEncoding.base64Url(), plaintext, dirId);
90105
return ciphertext + UVF_FILE_EXT;

src/main/java/org/cryptomator/cryptolib/v3/DirectoryMetadataImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ public DirectoryMetadataImpl(int seedId, byte[] dirId) {
1212
this.dirId = dirId;
1313
}
1414

15+
static DirectoryMetadataImpl cast(DirectoryMetadata metadata) {
16+
if (metadata instanceof DirectoryMetadataImpl) {
17+
return (DirectoryMetadataImpl) metadata;
18+
} else {
19+
throw new IllegalArgumentException("Unsupported metadata type " + metadata.getClass());
20+
}
21+
}
22+
1523
public byte[] dirId() {
1624
return dirId;
1725
}

0 commit comments

Comments
 (0)