11package org .cryptomator .cryptofs ;
22
33import org .cryptomator .cryptofs .common .Constants ;
4+ import org .cryptomator .cryptolib .api .CryptoException ;
45import org .cryptomator .cryptolib .api .Cryptor ;
6+ import org .cryptomator .cryptolib .common .DecryptingReadableByteChannel ;
57import org .cryptomator .cryptolib .common .EncryptingWritableByteChannel ;
68
79import javax .inject .Inject ;
1012import java .nio .channels .ByteChannel ;
1113import java .nio .charset .StandardCharsets ;
1214import java .nio .file .Files ;
15+ import java .nio .file .Path ;
1316import java .nio .file .StandardOpenOption ;
1417
1518/**
16- * Single purpose class to back up the directory id of an encrypted directory when it is created .
19+ * Single purpose class to read or write the directory id backup of an encrypted directory.
1720 */
1821@ CryptoFileSystemScoped
1922public class DirectoryIdBackup {
2023
21- private Cryptor cryptor ;
24+ private final Cryptor cryptor ;
2225
2326 @ Inject
2427 public DirectoryIdBackup (Cryptor cryptor ) {
2528 this .cryptor = cryptor ;
2629 }
2730
2831 /**
29- * Performs the backup operation for the given {@link CiphertextDirectory} object.
32+ * Writes the dirId backup file for the {@link CiphertextDirectory} object.
3033 * <p>
31- * The directory id is written via an encrypting channel to the file {@link CiphertextDirectory#path()} / {@value Constants#DIR_BACKUP_FILE_NAME}.
34+ * The directory id is written via an encrypting channel to the file {@link CiphertextDirectory#path()}.resolve( {@value Constants#DIR_ID_BACKUP_FILE_NAME});
3235 *
3336 * @param ciphertextDirectory The cipher dir object containing the dir id and the encrypted content root
3437 * @throws IOException if an IOException is raised during the write operation
3538 */
36- public void execute (CiphertextDirectory ciphertextDirectory ) throws IOException {
37- try (var channel = Files .newByteChannel (ciphertextDirectory .path (). resolve ( Constants . DIR_BACKUP_FILE_NAME ), StandardOpenOption .CREATE_NEW , StandardOpenOption .WRITE ); //
39+ public void write (CiphertextDirectory ciphertextDirectory ) throws IOException {
40+ try (var channel = Files .newByteChannel (getBackupFilePath ( ciphertextDirectory .path ()), StandardOpenOption .CREATE_NEW , StandardOpenOption .WRITE ); //
3841 var encryptingChannel = wrapEncryptionAround (channel , cryptor )) {
3942 encryptingChannel .write (ByteBuffer .wrap (ciphertextDirectory .dirId ().getBytes (StandardCharsets .US_ASCII )));
4043 }
@@ -43,16 +46,65 @@ public void execute(CiphertextDirectory ciphertextDirectory) throws IOException
4346 /**
4447 * Static method to explicitly back up the directory id for a specified ciphertext directory.
4548 *
46- * @param cryptor The cryptor to be used
49+ * @param cryptor The cryptor to be used for encryption
4750 * @param ciphertextDirectory A {@link CiphertextDirectory} for which the dirId should be back up'd.
4851 * @throws IOException when the dirId file already exists, or it cannot be written to.
4952 */
50- public static void backupManually (Cryptor cryptor , CiphertextDirectory ciphertextDirectory ) throws IOException {
51- new DirectoryIdBackup (cryptor ).execute (ciphertextDirectory );
53+ public static void write (Cryptor cryptor , CiphertextDirectory ciphertextDirectory ) throws IOException {
54+ new DirectoryIdBackup (cryptor ).write (ciphertextDirectory );
5255 }
5356
5457
55- static EncryptingWritableByteChannel wrapEncryptionAround (ByteChannel channel , Cryptor cryptor ) {
58+ /**
59+ * Reads the dirId backup file and retrieves the directory id from it.
60+ *
61+ * @param ciphertextContentDir path of a ciphertext <strong>content</strong> directory
62+ * @return a byte array containing the directory id
63+ * @throws IOException if the dirId backup file cannot be read
64+ * @throws CryptoException if the content of dirId backup file cannot be decrypted/authenticated
65+ * @throws IllegalStateException if the directory id exceeds {@value Constants#MAX_DIR_ID_LENGTH} chars
66+ */
67+ public byte [] read (Path ciphertextContentDir ) throws IOException , CryptoException , IllegalStateException {
68+ var dirIdBackupFile = getBackupFilePath (ciphertextContentDir );
69+ var dirIdBuffer = ByteBuffer .allocate (Constants .MAX_DIR_ID_LENGTH + 1 ); //a dir id contains at most 36 ascii chars, we add for security checks one more
70+
71+ try (var channel = Files .newByteChannel (dirIdBackupFile , StandardOpenOption .READ ); //
72+ var decryptingChannel = wrapDecryptionAround (channel , cryptor )) {
73+ int read = decryptingChannel .read (dirIdBuffer );
74+ if (read < 0 || read > Constants .MAX_DIR_ID_LENGTH ) {
75+ throw new IllegalStateException ("Read directory id exceeds the maximum length of %d characters" .formatted (Constants .MAX_DIR_ID_LENGTH ));
76+ }
77+ }
78+
79+ var dirId = new byte [dirIdBuffer .position ()];
80+ dirIdBuffer .get (0 , dirId );
81+ return dirId ;
82+ }
83+
84+ /**
85+ * Static method to explicitly retrieve the directory id of a ciphertext directory from the dirId backup file
86+ *
87+ * @param cryptor The cryptor to be used for decryption
88+ * @param ciphertextContentDir path of a ciphertext <strong>content</strong> directory
89+ * @return a byte array containing the directory id
90+ * @throws IOException if the dirId backup file cannot be read
91+ * @throws CryptoException if the content of dirId backup file cannot be decrypted/authenticated
92+ * @throws IllegalStateException if the directory id exceeds {@value Constants#MAX_DIR_ID_LENGTH} chars
93+ */
94+ public static byte [] read (Cryptor cryptor , Path ciphertextContentDir ) throws IOException , CryptoException , IllegalStateException {
95+ return new DirectoryIdBackup (cryptor ).read (ciphertextContentDir );
96+ }
97+
98+
99+ private static Path getBackupFilePath (Path ciphertextContentDir ) {
100+ return ciphertextContentDir .resolve (Constants .DIR_ID_BACKUP_FILE_NAME );
101+ }
102+
103+ DecryptingReadableByteChannel wrapDecryptionAround (ByteChannel channel , Cryptor cryptor ) {
104+ return new DecryptingReadableByteChannel (channel , cryptor , true );
105+ }
106+
107+ EncryptingWritableByteChannel wrapEncryptionAround (ByteChannel channel , Cryptor cryptor ) {
56108 return new EncryptingWritableByteChannel (channel , cryptor );
57109 }
58110}
0 commit comments