Encrypt and upload files
This commit is contained in:
parent
797c6ae856
commit
4b63196e34
8 changed files with 110 additions and 17 deletions
|
@ -1,4 +1,3 @@
|
|||
import 'package:encrypt/encrypt.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
@ -75,13 +74,13 @@ class Configuration {
|
|||
return _preferences.getBool(hasOptedForE2EKey);
|
||||
}
|
||||
|
||||
void generateAndSaveKey(String passphrase) async {
|
||||
final key = SecureRandom(32).base64;
|
||||
await _preferences.setString(keyKey, CryptoUtil.encrypt(key, passphrase));
|
||||
Future<void> generateAndSaveKey(String passphrase) async {
|
||||
final key = CryptoUtil.getBase64EncodedSecureRandomString(length: 32);
|
||||
await _preferences.setString(keyKey, key);
|
||||
}
|
||||
|
||||
String getKey(String passphrase) {
|
||||
return CryptoUtil.decrypt(_preferences.getString(keyKey), passphrase);
|
||||
return _preferences.getString(keyKey);
|
||||
}
|
||||
|
||||
bool hasConfiguredAccount() {
|
||||
|
|
|
@ -97,6 +97,19 @@ class File {
|
|||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> getMetadata() {
|
||||
final metadata = Map<String, dynamic>();
|
||||
metadata["localID"] = localID;
|
||||
metadata["title"] = title;
|
||||
metadata["deviceFolder"] = deviceFolder;
|
||||
metadata["creationTime"] = creationTime;
|
||||
metadata["modificationTime"] = modificationTime;
|
||||
metadata["latitude"] = location.latitude;
|
||||
metadata["longitude"] = location.longitude;
|
||||
metadata["fileType"] = fileType.index;
|
||||
return metadata;
|
||||
}
|
||||
|
||||
String getDownloadUrl() {
|
||||
return Configuration.instance.getHttpEndpoint() +
|
||||
"/files/download/" +
|
||||
|
|
|
@ -4,6 +4,17 @@ enum FileType {
|
|||
other,
|
||||
}
|
||||
|
||||
int getInt(FileType fileType) {
|
||||
switch (fileType) {
|
||||
case FileType.image:
|
||||
return 0;
|
||||
case FileType.video:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
FileType getFileType(int fileType) {
|
||||
switch (fileType) {
|
||||
case 0:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'dart:io' as io;
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
|
@ -11,6 +12,7 @@ import 'package:photos/file_repository.dart';
|
|||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:photos/models/file_type.dart';
|
||||
import 'package:photos/utils/crypto_util.dart';
|
||||
import 'package:photos/utils/file_name_util.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
@ -26,6 +28,7 @@ class PhotoSyncManager {
|
|||
bool _isSyncInProgress = false;
|
||||
Future<void> _existingSync;
|
||||
SharedPreferences _prefs;
|
||||
String _encryptedFilesDirectory;
|
||||
|
||||
static final _lastSyncTimeKey = "last_sync_time";
|
||||
static final _lastDBUpdationTimeKey = "last_db_updation_time";
|
||||
|
@ -76,8 +79,8 @@ class PhotoSyncManager {
|
|||
var lastDBUpdationTime = _prefs.getInt(_lastDBUpdationTimeKey);
|
||||
if (lastDBUpdationTime == null) {
|
||||
lastDBUpdationTime = 0;
|
||||
await _initializeDirectories();
|
||||
}
|
||||
await _initializeDirectories();
|
||||
|
||||
final pathEntities =
|
||||
await _getGalleryList(lastDBUpdationTime, syncStartTime);
|
||||
|
@ -181,7 +184,12 @@ class PhotoSyncManager {
|
|||
}
|
||||
_logger.info("Uploading " + file.toString());
|
||||
try {
|
||||
final uploadedFile = await _uploadFile(file);
|
||||
var uploadedFile;
|
||||
if (Configuration.instance.hasOptedForE2E()) {
|
||||
uploadedFile = await _uploadEncryptedFile(file);
|
||||
} else {
|
||||
uploadedFile = await _uploadFile(file);
|
||||
}
|
||||
await _db.update(file.generatedID, uploadedFile.uploadedFileID,
|
||||
uploadedFile.updationTime);
|
||||
_prefs.setInt(_lastSyncTimeKey, uploadedFile.updationTime);
|
||||
|
@ -236,6 +244,42 @@ class PhotoSyncManager {
|
|||
}
|
||||
}
|
||||
|
||||
Future<File> _uploadEncryptedFile(File file) async {
|
||||
final filePath = (await (await file.getAsset()).originFile).path;
|
||||
final encryptedFileName = file.generatedID.toString() + ".aes";
|
||||
final encryptedFilePath = _encryptedFilesDirectory + encryptedFileName;
|
||||
final fileIV = CryptoUtil.getBase64EncodedSecureRandomString(length: 16);
|
||||
final key = Configuration.instance.getKey("hello");
|
||||
await CryptoUtil.encryptFile(filePath, encryptedFilePath, key, fileIV);
|
||||
|
||||
final metadata = jsonEncode(file.getMetadata());
|
||||
final metadataIV =
|
||||
CryptoUtil.getBase64EncodedSecureRandomString(length: 16);
|
||||
final encryptedMetadata = CryptoUtil.encrypt(metadata, key, metadataIV);
|
||||
final formData = FormData.fromMap({
|
||||
"file": MultipartFile.fromFileSync(encryptedFilePath,
|
||||
filename: encryptedFileName),
|
||||
"fileIV": fileIV,
|
||||
"metadata": encryptedMetadata,
|
||||
"metadataIV": metadataIV,
|
||||
});
|
||||
return _dio
|
||||
.post(
|
||||
Configuration.instance.getHttpEndpoint() + "/encrypted-files",
|
||||
options:
|
||||
Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),
|
||||
data: formData,
|
||||
)
|
||||
.then((response) {
|
||||
io.File(encryptedFilePath).deleteSync();
|
||||
final data = response.data;
|
||||
file.uploadedFileID = data["id"];
|
||||
file.updationTime = data["updationTime"];
|
||||
file.ownerID = data["ownerID"];
|
||||
return file;
|
||||
});
|
||||
}
|
||||
|
||||
Future<File> _uploadFile(File localPhoto) async {
|
||||
final title = getJPGFileNameForHEIC(localPhoto);
|
||||
final formData = FormData.fromMap({
|
||||
|
@ -283,8 +327,8 @@ class PhotoSyncManager {
|
|||
|
||||
Future _initializeDirectories() async {
|
||||
final externalPath = (await getApplicationDocumentsDirectory()).path;
|
||||
new Directory(externalPath + "/photos/thumbnails")
|
||||
.createSync(recursive: true);
|
||||
_encryptedFilesDirectory = externalPath + "/encrypted/files/";
|
||||
new io.Directory(_encryptedFilesDirectory).createSync(recursive: true);
|
||||
}
|
||||
|
||||
Future<bool> _insertFilesToDB(List<File> files, int timestamp) async {
|
||||
|
|
|
@ -338,9 +338,9 @@ class _PassphraseWidgetState extends State<PassphraseWidget> {
|
|||
actions: <Widget>[
|
||||
CupertinoDialogAction(
|
||||
child: Text('Save'),
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
Configuration.instance.generateAndSaveKey(_passphrase);
|
||||
await Configuration.instance.generateAndSaveKey(_passphrase);
|
||||
Bus.instance.fire(UserAuthenticatedEvent());
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,13 +1,31 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:aes_crypt/aes_crypt.dart';
|
||||
import 'package:encrypt/encrypt.dart';
|
||||
|
||||
class CryptoUtil {
|
||||
static String encrypt(String plainText, String key) {
|
||||
final encrypter = Encrypter(AES(Key.fromUtf8(key)));
|
||||
return encrypter.encrypt(plainText).base64;
|
||||
static String getBase64EncodedSecureRandomString({int length = 32}) {
|
||||
return SecureRandom(length).base64;
|
||||
}
|
||||
|
||||
static String decrypt(String cipherText, String key) {
|
||||
final encrypter = Encrypter(AES(Key.fromUtf8(key)));
|
||||
static String encrypt(String plainText, String base64Key, String base64IV) {
|
||||
final encrypter = Encrypter(AES(Key.fromBase64(base64Key)));
|
||||
final iv = base64IV == null ? null : IV.fromBase64(base64IV);
|
||||
return encrypter.encrypt(plainText, iv: iv).base64;
|
||||
}
|
||||
|
||||
static String decrypt(String cipherText, String base64Key) {
|
||||
final encrypter = Encrypter(AES(Key.fromBase64(base64Key)));
|
||||
return encrypter.decrypt(Encrypted.fromBase64(cipherText));
|
||||
}
|
||||
|
||||
// Encrypts a file and returns the IV that was used
|
||||
static Future<void> encryptFile(String sourcePath, String destinationPath,
|
||||
String base64Key, String base64IV) async {
|
||||
final encrypter = AesCrypt("hello");
|
||||
encrypter.aesSetParams(Key.fromBase64(base64Key).bytes,
|
||||
IV.fromBase64(base64IV).bytes, AesMode.cbc);
|
||||
encrypter.setOverwriteMode(AesCryptOwMode.on);
|
||||
await encrypter.encryptFile(sourcePath, destinationPath);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
aes_crypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: aes_crypt
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
animate_do:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -23,6 +23,7 @@ dependencies:
|
|||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
encrypt: ^4.0.2
|
||||
aes_crypt: ^0.1.1
|
||||
cupertino_icons: ^0.1.2
|
||||
photo_manager: ^0.5.7
|
||||
provider: ^3.1.0
|
||||
|
|
Loading…
Add table
Reference in a new issue