Просмотр исходного кода

Encrypt every file with a separate key

Vishnu Mohandas 4 лет назад
Родитель
Сommit
a5d3305cbf
5 измененных файлов с 95 добавлено и 46 удалено
  1. 30 5
      lib/db/files_db.dart
  2. 9 5
      lib/file_upload_manager.dart
  3. 11 0
      lib/models/file.dart
  4. 41 32
      lib/photo_sync_manager.dart
  5. 4 4
      lib/utils/file_util.dart

+ 30 - 5
lib/db/files_db.dart

@@ -31,6 +31,8 @@ class FilesDB {
   static final columnCreationTime = 'creation_time';
   static final columnModificationTime = 'modification_time';
   static final columnUpdationTime = 'updation_time';
+  static final columnEncryptedKey = 'encrypted_key';
+  static final columnIV = 'iv';
 
   // make this a singleton class
   FilesDB._privateConstructor();
@@ -72,6 +74,8 @@ class FilesDB {
             $columnCreationTime TEXT NOT NULL,
             $columnModificationTime TEXT NOT NULL,
             $columnUpdationTime TEXT
+            $columnEncryptedKey TEXT
+            $columnIV TEXT
           )
           ''');
   }
@@ -170,21 +174,30 @@ class FilesDB {
     return _convertToFiles(results);
   }
 
-  Future<File> getMatchingFile(String localID, String title,
-      String deviceFolder, int creationTime, int modificationTime,
+  Future<File> getMatchingFile(
+      String localID,
+      String title,
+      String deviceFolder,
+      int creationTime,
+      int modificationTime,
+      String encryptedKey,
+      String iv,
       {String alternateTitle}) async {
     final db = await instance.database;
     final rows = await db.query(
       table,
       where: '''$columnLocalID=? AND ($columnTitle=? OR $columnTitle=?) AND 
-          $columnDeviceFolder=? AND $columnCreationTime=? AND $columnModificationTime=?''',
+          $columnDeviceFolder=? AND $columnCreationTime=? AND 
+          $columnModificationTime=? AND $columnEncryptedKey AND $columnIV''',
       whereArgs: [
         localID,
         title,
         alternateTitle,
         deviceFolder,
         creationTime,
-        modificationTime
+        modificationTime,
+        encryptedKey,
+        iv,
       ],
     );
     if (rows.isNotEmpty) {
@@ -208,11 +221,19 @@ class FilesDB {
     }
   }
 
-  Future<int> update(int generatedID, int uploadedID, int updationTime) async {
+  Future<int> update(
+    int generatedID,
+    int uploadedID,
+    int updationTime,
+    String encryptedKey,
+    String iv,
+  ) async {
     final db = await instance.database;
     final values = new Map<String, dynamic>();
     values[columnUploadedFileID] = uploadedID;
     values[columnUpdationTime] = updationTime;
+    values[columnEncryptedKey] = encryptedKey;
+    values[columnIV] = iv;
     return await db.update(
       table,
       values,
@@ -364,6 +385,8 @@ class FilesDB {
     row[columnCreationTime] = file.creationTime;
     row[columnModificationTime] = file.modificationTime;
     row[columnUpdationTime] = file.updationTime;
+    row[columnEncryptedKey] = file.encryptedKey;
+    row[columnIV] = file.iv;
     return row;
   }
 
@@ -386,6 +409,8 @@ class FilesDB {
     file.updationTime = row[columnUpdationTime] == null
         ? -1
         : int.parse(row[columnUpdationTime]);
+    file.encryptedKey = row[columnEncryptedKey];
+    file.iv = row[columnIV];
     return file;
   }
 }

+ 9 - 5
lib/file_upload_manager.dart

@@ -41,7 +41,10 @@ class FileUploadManager {
   }
 
   Future<File> encryptAndUploadFile(File file) async {
-    final key = Configuration.instance.getKey();
+    final key = CryptoUtil.getBase64EncodedSecureRandomString(length: 32);
+    final iv = CryptoUtil.getBase64EncodedSecureRandomString(length: 16);
+    final encryptedKey =
+        CryptoUtil.encryptToBase64(key, Configuration.instance.getKey(), iv);
 
     final encryptedFileName = file.generatedID.toString() + ".aes";
     final tempDirectory = Configuration.instance.getTempDirectory();
@@ -66,15 +69,14 @@ class FileUploadManager {
         await putFile(thumbnailUploadURL, io.File(encryptedThumbnailPath));
 
     final metadata = jsonEncode(file.getMetadata());
-    final metadataIV =
-        CryptoUtil.getBase64EncodedSecureRandomString(length: 16);
     final encryptedMetadata =
-        CryptoUtil.encryptToBase64(metadata, key, metadataIV);
+        CryptoUtil.encryptDataToData(utf8.encode(metadata), key);
     final data = {
       "fileObjectKey": fileObjectKey,
       "thumbnailObjectKey": thumbnailObjectKey,
       "metadata": encryptedMetadata,
-      "metadataIV": metadataIV,
+      "encryptedKey": encryptedKey,
+      "iv": iv,
     };
     return _dio
         .post(
@@ -90,6 +92,8 @@ class FileUploadManager {
       file.uploadedFileID = data["id"];
       file.updationTime = data["updationTime"];
       file.ownerID = data["ownerID"];
+      file.encryptedKey = encryptedKey;
+      file.iv = iv;
       return file;
     });
   }

+ 11 - 0
lib/models/file.dart

@@ -3,6 +3,7 @@ import 'package:path/path.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/models/file_type.dart';
 import 'package:photos/models/location.dart';
+import 'package:photos/utils/crypto_util.dart';
 
 class File {
   int generatedID;
@@ -18,6 +19,8 @@ class File {
   int updationTime;
   Location location;
   FileType fileType;
+  String encryptedKey;
+  String iv;
 
   File();
 
@@ -130,6 +133,14 @@ class File {
         Configuration.instance.getToken();
   }
 
+  String getKey() {
+    if (encryptedKey == null) {
+      return null;
+    }
+    return CryptoUtil.decryptFromBase64(
+        encryptedKey, Configuration.instance.getKey(), iv);
+  }
+
   @override
   String toString() {
     return '''File(generatedId: $generatedID, uploadedFileId: $uploadedFileID, 

+ 41 - 32
lib/photo_sync_manager.dart

@@ -178,7 +178,7 @@ class PhotoSyncManager {
   Future<void> _downloadEncryptedFilesDiff() async {
     final diff =
         await _getEncryptedFilesDiff(_getEncryptedFilesSyncTime(), _diffLimit);
-    if (diff != null && diff.isNotEmpty) {
+    if (diff.isNotEmpty) {
       await _storeDiff(diff, _encryptedFilesSyncTimeKey);
       FileRepository.instance.reloadFiles();
       if (diff.length == _diffLimit) {
@@ -211,9 +211,8 @@ class PhotoSyncManager {
           uploadedFile = await _uploadManager.uploadFile(file);
         }
         await _db.update(file.generatedID, uploadedFile.uploadedFileID,
-            uploadedFile.updationTime);
+            uploadedFile.updationTime, file.encryptedKey, file.iv);
         _prefs.setInt(_syncTimeKey, uploadedFile.updationTime);
-
         Bus.instance.fire(PhotoUploadEvent(
             completed: i + 1, total: photosToBeUploaded.length));
       } catch (e) {
@@ -232,9 +231,11 @@ class PhotoSyncManager {
             file.deviceFolder,
             file.creationTime,
             file.modificationTime,
+            file.encryptedKey,
+            file.iv,
             alternateTitle: getHEICFileNameForJPG(file));
-        await _db.update(
-            existingPhoto.generatedID, file.uploadedFileID, file.updationTime);
+        await _db.update(existingPhoto.generatedID, file.uploadedFileID,
+            file.updationTime, file.encryptedKey, file.iv);
       } catch (e) {
         file.localID = null; // File uploaded from a different device
         await _db.insert(file);
@@ -265,33 +266,41 @@ class PhotoSyncManager {
   }
 
   Future<List<File>> _getEncryptedFilesDiff(int lastSyncTime, int limit) async {
-    Response response = await _dio.get(
-      Configuration.instance.getHttpEndpoint() + "/encrypted-files/diff",
-      options:
-          Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
-      queryParameters: {
-        "sinceTimestamp": lastSyncTime,
-        "limit": limit,
-      },
-    ).catchError((e) => _logger.severe(e));
-    if (response != null) {
-      Bus.instance.fire(RemoteSyncEvent(true));
-      return (response.data["diff"] as List).map((json) {
-        final file = File();
-        file.uploadedFileID = json["id"];
-        file.ownerID = json["ownerID"];
-        file.updationTime = json["updationTime"];
-        file.isEncrypted = true;
-        final String metadataIV = json["metadataIV"];
-        Map<String, dynamic> metadata = jsonDecode(CryptoUtil.decryptFromBase64(
-            json["metadata"], Configuration.instance.getKey(), metadataIV));
-        file.applyMetadata(metadata);
-        return file;
-      }).toList();
-    } else {
-      Bus.instance.fire(RemoteSyncEvent(false));
-      return null;
-    }
+    return _dio
+        .get(
+          Configuration.instance.getHttpEndpoint() + "/encrypted-files/diff",
+          queryParameters: {
+            "token": Configuration.instance.getToken(),
+            "sinceTimestamp": lastSyncTime,
+            "limit": limit,
+          },
+        )
+        .catchError((e) => _logger.severe(e))
+        .then((response) async {
+          final files = List<File>();
+          if (response != null) {
+            Bus.instance.fire(RemoteSyncEvent(true));
+            final diff = response.data["diff"] as List;
+            for (final json in diff) {
+              final file = File();
+              file.uploadedFileID = json["id"];
+              file.ownerID = json["ownerID"];
+              file.updationTime = json["updationTime"];
+              file.isEncrypted = true;
+              file.encryptedKey = json["encryptedKey"];
+              file.iv = json["iv"];
+              final key = CryptoUtil.decryptFromBase64(
+                  file.encryptedKey, Configuration.instance.getKey(), file.iv);
+              Map<String, dynamic> metadata = jsonDecode(
+                  await CryptoUtil.decryptDataToData(json["metadata"], key));
+              file.applyMetadata(metadata);
+              files.add(file);
+            }
+          } else {
+            Bus.instance.fire(RemoteSyncEvent(false));
+          }
+          return files;
+        });
   }
 
   Future<void> _deletePhotosOnServer() async {

+ 4 - 4
lib/utils/file_util.dart

@@ -149,8 +149,8 @@ Future<io.File> _downloadAndDecrypt(File file, BaseCacheManager cacheManager,
     onReceiveProgress: progressCallback,
   )
       .then((_) async {
-    final data = await CryptoUtil.decryptFileToData(
-        temporaryPath, Configuration.instance.getKey());
+    final data =
+        await CryptoUtil.decryptFileToData(temporaryPath, file.getKey());
     io.File(temporaryPath).deleteSync();
     return cacheManager.putFile(file.getDownloadUrl(), data);
   });
@@ -162,8 +162,8 @@ Future<io.File> _downloadAndDecryptThumbnail(File file) async {
       "_thumbnail.aes";
   Dio dio = Dio();
   return dio.download(file.getThumbnailUrl(), temporaryPath).then((_) async {
-    final data = await CryptoUtil.decryptFileToData(
-        temporaryPath, Configuration.instance.getKey());
+    final data =
+        await CryptoUtil.decryptFileToData(temporaryPath, file.getKey());
     io.File(temporaryPath).deleteSync();
     return ThumbnailCacheManager().putFile(file.getThumbnailUrl(), data);
   });