diff --git a/lib/utils/crypto_util.dart b/lib/utils/crypto_util.dart index 47d5b327a..7c7cfb9fa 100644 --- a/lib/utils/crypto_util.dart +++ b/lib/utils/crypto_util.dart @@ -38,7 +38,7 @@ EncryptionResult chachaEncryptFile(Map args) { logger.info("Encrypting file of size " + sourceFileLength.toString()); final inputFile = sourceFile.openSync(mode: io.FileMode.read); - final key = Sodium.cryptoSecretstreamXchacha20poly1305Keygen(); + final key = args["key"] ?? Sodium.cryptoSecretstreamXchacha20poly1305Keygen(); final initPushResult = Sodium.cryptoSecretstreamXchacha20poly1305InitPush(key); var bytesRead = 0; @@ -155,11 +155,13 @@ class CryptoUtil { static Future encryptFile( String sourceFilePath, - String destinationFilePath, - ) { + String destinationFilePath, { + Uint8List key, + }) { final args = Map(); args["sourceFilePath"] = sourceFilePath; args["destinationFilePath"] = destinationFilePath; + args["key"] = key; return Computer().compute(chachaEncryptFile, param: args); } diff --git a/lib/utils/file_uploader.dart b/lib/utils/file_uploader.dart index 7eff04fe7..a89d6e13d 100644 --- a/lib/utils/file_uploader.dart +++ b/lib/utils/file_uploader.dart @@ -4,13 +4,13 @@ import 'dart:convert'; import 'dart:io' as io; import 'package:connectivity/connectivity.dart'; import 'package:dio/dio.dart'; -import 'package:flutter_image_compress/flutter_image_compress.dart'; import 'package:flutter_sodium/flutter_sodium.dart'; import 'package:logging/logging.dart'; import 'package:photos/core/configuration.dart'; import 'package:photos/core/constants.dart'; import 'package:photos/core/network.dart'; import 'package:photos/db/files_db.dart'; +import 'package:photos/models/encryption_result.dart'; import 'package:photos/models/file.dart'; import 'package:photos/models/location.dart'; import 'package:photos/models/upload_url.dart'; @@ -152,13 +152,26 @@ class FileUploader { final encryptedThumbnailPath = tempDirectory + file.generatedID.toString() + "_thumbnail.encrypted"; final sourceFile = (await (await file.getAsset()).originFile); + try { + var key; + var isAlreadyUploadedFile = file.uploadedFileID != null; + if (isAlreadyUploadedFile) { + key = decryptFileKey(file); + } else { + key = null; + } + if (io.File(encryptedFilePath).existsSync()) { io.File(encryptedFilePath).deleteSync(); } final encryptedFile = io.File(encryptedFilePath); - final fileAttributes = - await CryptoUtil.encryptFile(sourceFile.path, encryptedFilePath); + + final fileAttributes = await CryptoUtil.encryptFile( + sourceFile.path, + encryptedFilePath, + key: key, + ); var thumbnailData = (await (await file.getAsset()).thumbDataWithSize( THUMBNAIL_LARGE_SIZE, @@ -201,15 +214,6 @@ class FileUploader { final encryptedMetadataData = CryptoUtil.encryptChaCha( utf8.encode(jsonEncode(file.getMetadata())), fileAttributes.key); - - final encryptedFileKeyData = CryptoUtil.encryptSync( - fileAttributes.key, - CollectionsService.instance.getCollectionKey(collectionID), - ); - - final encryptedKey = - Sodium.bin2base64(encryptedFileKeyData.encryptedData); - final keyDecryptionNonce = Sodium.bin2base64(encryptedFileKeyData.nonce); final fileDecryptionHeader = Sodium.bin2base64(fileAttributes.header); final thumbnailDecryptionHeader = Sodium.bin2base64(encryptedThumbnailData.header); @@ -217,41 +221,29 @@ class FileUploader { Sodium.bin2base64(encryptedMetadataData.encryptedData); final metadataDecryptionHeader = Sodium.bin2base64(encryptedMetadataData.header); - - final request = { - "collectionID": collectionID, - "encryptedKey": encryptedKey, - "keyDecryptionNonce": keyDecryptionNonce, - "file": { - "objectKey": fileObjectKey, - "decryptionHeader": fileDecryptionHeader, - }, - "thumbnail": { - "objectKey": thumbnailObjectKey, - "decryptionHeader": thumbnailDecryptionHeader, - }, - "metadata": { - "encryptedData": encryptedMetadata, - "decryptionHeader": metadataDecryptionHeader, - } - }; - final response = await _dio.post( - Configuration.instance.getHttpEndpoint() + "/files", - options: Options( - headers: {"X-Auth-Token": Configuration.instance.getToken()}), - data: request, - ); - final data = response.data; - file.uploadedFileID = data["id"]; - file.collectionID = collectionID; - file.updationTime = data["updationTime"]; - file.ownerID = data["ownerID"]; - file.encryptedKey = encryptedKey; - file.keyDecryptionNonce = keyDecryptionNonce; - file.fileDecryptionHeader = fileDecryptionHeader; - file.thumbnailDecryptionHeader = thumbnailDecryptionHeader; - file.metadataDecryptionHeader = metadataDecryptionHeader; - return file; + if (isAlreadyUploadedFile) { + return await _updateFile( + file, + fileObjectKey, + fileDecryptionHeader, + thumbnailObjectKey, + thumbnailDecryptionHeader, + encryptedMetadata, + metadataDecryptionHeader, + ); + } else { + return await _uploadFile( + file, + collectionID, + fileAttributes, + fileObjectKey, + fileDecryptionHeader, + thumbnailObjectKey, + thumbnailDecryptionHeader, + encryptedMetadata, + metadataDecryptionHeader, + ); + } } catch (e, s) { _logger.severe( "File upload failed for " + file.generatedID.toString(), e, s); @@ -269,6 +261,99 @@ class FileUploader { } } + Future _uploadFile( + File file, + int collectionID, + EncryptionResult fileAttributes, + String fileObjectKey, + String fileDecryptionHeader, + String thumbnailObjectKey, + String thumbnailDecryptionHeader, + String encryptedMetadata, + String metadataDecryptionHeader, + ) async { + final encryptedFileKeyData = CryptoUtil.encryptSync( + fileAttributes.key, + CollectionsService.instance.getCollectionKey(collectionID), + ); + final encryptedKey = Sodium.bin2base64(encryptedFileKeyData.encryptedData); + final keyDecryptionNonce = Sodium.bin2base64(encryptedFileKeyData.nonce); + final request = { + "collectionID": collectionID, + "encryptedKey": encryptedKey, + "keyDecryptionNonce": keyDecryptionNonce, + "file": { + "objectKey": fileObjectKey, + "decryptionHeader": fileDecryptionHeader, + }, + "thumbnail": { + "objectKey": thumbnailObjectKey, + "decryptionHeader": thumbnailDecryptionHeader, + }, + "metadata": { + "encryptedData": encryptedMetadata, + "decryptionHeader": metadataDecryptionHeader, + } + }; + final response = await _dio.post( + Configuration.instance.getHttpEndpoint() + "/files", + options: + Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}), + data: request, + ); + final data = response.data; + file.uploadedFileID = data["id"]; + file.collectionID = collectionID; + file.updationTime = data["updationTime"]; + file.ownerID = data["ownerID"]; + file.encryptedKey = encryptedKey; + file.keyDecryptionNonce = keyDecryptionNonce; + file.fileDecryptionHeader = fileDecryptionHeader; + file.thumbnailDecryptionHeader = thumbnailDecryptionHeader; + file.metadataDecryptionHeader = metadataDecryptionHeader; + return file; + } + + Future _updateFile( + File file, + String fileObjectKey, + String fileDecryptionHeader, + String thumbnailObjectKey, + String thumbnailDecryptionHeader, + String encryptedMetadata, + String metadataDecryptionHeader, + ) async { + final request = { + "id": file.uploadedFileID, + "file": { + "objectKey": fileObjectKey, + "decryptionHeader": fileDecryptionHeader, + }, + "thumbnail": { + "objectKey": thumbnailObjectKey, + "decryptionHeader": thumbnailDecryptionHeader, + }, + "metadata": { + "encryptedData": encryptedMetadata, + "decryptionHeader": metadataDecryptionHeader, + } + }; + final response = await _dio.post( + Configuration.instance.getHttpEndpoint() + "/files", + options: + Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}), + data: request, + ); + final data = response.data; + file.uploadedFileID = data["id"]; + file.updationTime = data["updationTime"]; + file.ownerID = data["ownerID"]; + file.fileDecryptionHeader = fileDecryptionHeader; + file.thumbnailDecryptionHeader = thumbnailDecryptionHeader; + file.metadataDecryptionHeader = metadataDecryptionHeader; + return file; + } + Future _getUploadURL() async { if (_uploadURLs.isEmpty) { await _fetchUploadURLs();