diff --git a/mobile/lib/services/machine_learning/file_ml/remote_fileml_service.dart b/mobile/lib/services/machine_learning/file_ml/remote_fileml_service.dart index 7fb62155e2..3e9345f72b 100644 --- a/mobile/lib/services/machine_learning/file_ml/remote_fileml_service.dart +++ b/mobile/lib/services/machine_learning/file_ml/remote_fileml_service.dart @@ -1,6 +1,4 @@ import "dart:async"; -import "dart:convert"; -import "dart:io"; import "package:computer/computer.dart"; import "package:flutter/foundation.dart" show Uint8List; @@ -11,8 +9,8 @@ import "package:photos/models/file/file.dart"; import 'package:photos/services/machine_learning/file_ml/file_ml.dart'; import "package:photos/services/machine_learning/file_ml/files_ml_data_response.dart"; import "package:photos/services/machine_learning/file_ml/remote_embedding.dart"; -import "package:photos/utils/crypto_util.dart"; import "package:photos/utils/file_download_util.dart"; +import "package:photos/utils/gzip.dart"; import "package:shared_preferences/shared_preferences.dart"; class RemoteFileMLService { @@ -36,22 +34,18 @@ class RemoteFileMLService { }) async { fileML.putClipIfNotNull(clipEmbedding); fileML.putFaceIfNotNull(faceEmbedding); - final encryptionKey = getFileKey(file); - final embeddingJSON = jsonEncode(fileML.remoteRawData); - final compressedData = gzipUInt8List(utf8.encode(embeddingJSON)); - final encryptedEmbedding = - await CryptoUtil.encryptChaCha(compressedData, encryptionKey); - final encryptedData = - CryptoUtil.bin2base64(encryptedEmbedding.encryptedData!); - final header = CryptoUtil.bin2base64(encryptedEmbedding.header!); + final ChaChaEncryptionResult encryptionResult = await gzipAndEncryptJson( + fileML.remoteRawData, + getFileKey(file), + ); try { final _ = await _dio.put( "/embeddings", data: { "fileID": file.uploadedFileID!, "model": 'ggml-clip', - "encryptedEmbedding": encryptedData, - "decryptionHeader": header, + "encryptedEmbedding": encryptionResult.encData, + "decryptionHeader": encryptionResult.header, }, ); } catch (e, s) { @@ -125,39 +119,21 @@ class RemoteFileMLService { } } -Uint8List ungzipUint8List(Uint8List compressedData) { - final codec = GZipCodec(); - final List decompressedList = codec.decode(compressedData); - return Uint8List.fromList(decompressedList); -} - -// gzipUInt8List -Uint8List gzipUInt8List(Uint8List data) { - final codec = GZipCodec(); - final compressedData = codec.encode(data); - return Uint8List.fromList(compressedData); -} - Future> _decryptFileMLComputer( Map args, ) async { final result = {}; final inputs = args["inputs"] as List; for (final input in inputs) { - final decryptArgs = {}; - decryptArgs["source"] = - CryptoUtil.base642bin(input.embedding.encryptedEmbedding); - decryptArgs["key"] = input.decryptionKey; - decryptArgs["header"] = - CryptoUtil.base642bin(input.embedding.decryptionHeader); - final embeddingData = chachaDecryptData(decryptArgs); - final unzippedData = ungzipUint8List(embeddingData); - final decodedJson = jsonDecode(utf8.decode(unzippedData)); - final RemoteFileML decodedEmbedding = RemoteFileML.fromRemote( - input.embedding.fileID, - decodedJson as Map, + final decodedJson = decryptAndUnzipJsonSync( + input.decryptionKey, + encryptedData: input.embedding.encryptedEmbedding, + header: input.embedding.decryptionHeader, + ); + result[input.embedding.fileID] = RemoteFileML.fromRemote( + input.embedding.fileID, + decodedJson, ); - result[input.embedding.fileID] = decodedEmbedding; } return result; } diff --git a/mobile/lib/utils/gzip.dart b/mobile/lib/utils/gzip.dart index e69de29bb2..d1680d43e7 100644 --- a/mobile/lib/utils/gzip.dart +++ b/mobile/lib/utils/gzip.dart @@ -0,0 +1,84 @@ +import "dart:convert"; +import "dart:io"; + +import "package:computer/computer.dart"; +import "package:flutter/foundation.dart"; +import "package:photos/utils/crypto_util.dart"; + +class ChaChaEncryptionResult { + final String encData; + final String header; + + ChaChaEncryptionResult({ + required this.encData, + required this.header, + }); +} + +Uint8List _unGzipUInt8List(Uint8List compressedData) { + final codec = GZipCodec(); + final List decompressedList = codec.decode(compressedData); + return Uint8List.fromList(decompressedList); +} + +// gzipUInt8List +Uint8List _gzipUInt8List(Uint8List data) { + final codec = GZipCodec(); + final compressedData = codec.encode(data); + return Uint8List.fromList(compressedData); +} + +Map decryptAndUnzipJsonSync( + Uint8List key, { + required String encryptedData, + required String header, +}) { + final decryptedData = chachaDecryptData({ + "source": CryptoUtil.base642bin(encryptedData), + "key": key, + "header": CryptoUtil.base642bin(header), + }); + final decompressedData = _unGzipUInt8List(decryptedData); + final json = utf8.decode(decompressedData); + return jsonDecode(json); +} + +// zipJsonAndEncryptSync performs all operations synchronously, on a single isolate. +ChaChaEncryptionResult gzipAndEncryptJsonSync( + Map jsonData, + Uint8List key, +) { + final json = utf8.encode(jsonEncode(jsonData)); + final compressedJson = _gzipUInt8List(Uint8List.fromList(json)); + final encryptedData = chachaEncryptData({ + "source": compressedJson, + "key": key, + }); + return ChaChaEncryptionResult( + encData: CryptoUtil.bin2base64(encryptedData.encryptedData!), + header: CryptoUtil.bin2base64(encryptedData.header!), + ); +} + +Future gzipAndEncryptJson( + Map jsonData, + Uint8List key, +) async { + final Computer computer = Computer.shared(); + final response = + await computer.compute, ChaChaEncryptionResult>( + _gzipAndEncryptJsonSync, + param: { + "jsonData": jsonData, + "key": key, + }, + taskName: "gzipAndEncryptJson", + ); + return response; +} + +ChaChaEncryptionResult _gzipAndEncryptJsonSync( + Map args, +) { + return gzipAndEncryptJsonSync(args["jsonData"], args["key"]); +}