Encrypt and upload files

This commit is contained in:
Vishnu Mohandas 2020-08-11 05:17:22 +05:30
parent 797c6ae856
commit 4b63196e34
8 changed files with 110 additions and 17 deletions

View file

@ -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() {

View file

@ -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/" +

View file

@ -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:

View file

@ -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 {

View file

@ -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());
},
)

View file

@ -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);
}
}

View file

@ -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:

View file

@ -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