Encrypt every file with a separate key
This commit is contained in:
parent
aa2890107f
commit
a5d3305cbf
5 changed files with 95 additions and 46 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue