Encrypt every file with a separate key

This commit is contained in:
Vishnu Mohandas 2020-09-03 22:20:26 +05:30
parent aa2890107f
commit a5d3305cbf
5 changed files with 95 additions and 46 deletions

View file

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

View file

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

View file

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

View file

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

View file

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