Write backup to encrypted zip.

This commit is contained in:
Matthew Chen 2018-01-04 15:16:24 -05:00
parent 221a211156
commit 8769fb07cf
6 changed files with 263 additions and 7 deletions

View File

@ -31,6 +31,7 @@ end
target 'Signal' do
shared_pods
pod 'ATAppUpdater', :inhibit_warnings => true
pod 'SSZipArchive', :inhibit_warnings => true
target 'SignalTests' do
inherit! :search_paths

View File

@ -59,6 +59,7 @@ PODS:
- SQLCipher/common (3.4.1)
- SQLCipher/standard (3.4.1):
- SQLCipher/common
- SSZipArchive (2.1.1)
- TwistedOakCollapsingFutures (1.0.0):
- UnionFind (~> 1.0)
- UnionFind (1.0.1)
@ -138,6 +139,7 @@ DEPENDENCIES:
- Reachability
- SignalServiceKit (from `.`)
- SocketRocket (from `https://github.com/facebook/SocketRocket.git`)
- SSZipArchive
- YapDatabase/SQLCipher
- YYImage
@ -200,11 +202,12 @@ SPEC CHECKSUMS:
SignalServiceKit: 8f9038e584080bee8c367268067e6e0ec0feefcf
SocketRocket: dbb1554b8fc288ef8ef370d6285aeca7361be31e
SQLCipher: 43d12c0eb9c57fb438749618fc3ce0065509a559
SSZipArchive: 14401ade5f8e82aba1ff03e9f88e9de60937ae60
TwistedOakCollapsingFutures: f359b90f203e9ab13dfb92c9ff41842a7fe1cd0c
UnionFind: c33be5adb12983981d6e827ea94fc7f9e370f52d
YapDatabase: 299a32de9d350d37a9ac5b0532609d87d5d2a5de
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
PODFILE CHECKSUM: bbc38aeceb39482a3cc46f0de44c6aef671e3e9f
PODFILE CHECKSUM: fe273b089523b52e20652cebcf4854a80934f46c
COCOAPODS: 1.3.1

View File

@ -119,6 +119,7 @@
347850721FDAEB17007B8332 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 347850701FDAEB16007B8332 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; };
3497DBEC1ECE257500DB2605 /* OWSCountryMetadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 3497DBEB1ECE257500DB2605 /* OWSCountryMetadata.m */; };
3497DBEF1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3497DBEE1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m */; };
34A910601FFEB114000C4745 /* OWSBackup.m in Sources */ = {isa = PBXBuildFile; fileRef = 34A9105F1FFEB114000C4745 /* OWSBackup.m */; };
34B0796D1FCF46B100E248C2 /* MainAppContext.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B0796B1FCF46B000E248C2 /* MainAppContext.m */; };
34B3F8711E8DF1700035BE1A /* AboutTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8351E8DF1700035BE1A /* AboutTableViewController.m */; };
34B3F8721E8DF1700035BE1A /* AdvancedSettingsTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8371E8DF1700035BE1A /* AdvancedSettingsTableViewController.m */; };
@ -603,6 +604,8 @@
3497DBEB1ECE257500DB2605 /* OWSCountryMetadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSCountryMetadata.m; sourceTree = "<group>"; };
3497DBED1ECE2E4700DB2605 /* DomainFrontingCountryViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DomainFrontingCountryViewController.h; sourceTree = "<group>"; };
3497DBEE1ECE2E4700DB2605 /* DomainFrontingCountryViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DomainFrontingCountryViewController.m; sourceTree = "<group>"; };
34A9105E1FFEB113000C4745 /* OWSBackup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSBackup.h; sourceTree = "<group>"; };
34A9105F1FFEB114000C4745 /* OWSBackup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSBackup.m; sourceTree = "<group>"; };
34B0796B1FCF46B000E248C2 /* MainAppContext.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MainAppContext.m; sourceTree = "<group>"; };
34B0796C1FCF46B000E248C2 /* MainAppContext.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainAppContext.h; sourceTree = "<group>"; };
34B0796E1FD07B1E00E248C2 /* SignalShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SignalShareExtension.entitlements; sourceTree = "<group>"; };
@ -1703,8 +1706,8 @@
76EB04C818170B33006006FC /* util */ = {
isa = PBXGroup;
children = (
B6DA6B061B8A2F9A00CA6F98 /* AppStoreRating.m */,
B6DA6B051B8A2F9A00CA6F98 /* AppStoreRating.h */,
B6DA6B061B8A2F9A00CA6F98 /* AppStoreRating.m */,
34CCAF361F0C0599004084F4 /* AppUpdateNag.h */,
34CCAF371F0C0599004084F4 /* AppUpdateNag.m */,
B90418E4183E9DD40038554A /* DateUtil.h */,
@ -1717,6 +1720,8 @@
76EB04EC18170B33006006FC /* NumberUtil.h */,
76EB04ED18170B33006006FC /* NumberUtil.m */,
34D99C911F2937CC00D284D6 /* OWSAnalytics.swift */,
34A9105E1FFEB113000C4745 /* OWSBackup.h */,
34A9105F1FFEB114000C4745 /* OWSBackup.m */,
4579431C1E7C8CE9008ED0C0 /* Pastelog.h */,
4579431D1E7C8CE9008ED0C0 /* Pastelog.m */,
450DF2041E0D74AC003D14BE /* Platform.swift */,
@ -2527,6 +2532,7 @@
"${BUILT_PRODUCTS_DIR}/Reachability/Reachability.framework",
"${BUILT_PRODUCTS_DIR}/SAMKeychain/SAMKeychain.framework",
"${BUILT_PRODUCTS_DIR}/SQLCipher/SQLCipher.framework",
"${BUILT_PRODUCTS_DIR}/SSZipArchive/SSZipArchive.framework",
"${BUILT_PRODUCTS_DIR}/SignalServiceKit/SignalServiceKit.framework",
"${BUILT_PRODUCTS_DIR}/SocketRocket/SocketRocket.framework",
"${BUILT_PRODUCTS_DIR}/TwistedOakCollapsingFutures/TwistedOakCollapsingFutures.framework",
@ -2552,6 +2558,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reachability.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SAMKeychain.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SQLCipher.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SSZipArchive.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SignalServiceKit.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SocketRocket.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/TwistedOakCollapsingFutures.framework",
@ -2945,6 +2952,7 @@
34E8BF381EE9E2FD00F5F4CA /* FingerprintViewScanController.m in Sources */,
346B66311F4E29B200E5122F /* CropScaleImageViewController.swift in Sources */,
45E5A6991F61E6DE001E4A8A /* MarqueeLabel.swift in Sources */,
34A910601FFEB114000C4745 /* OWSBackup.m in Sources */,
34D1F0B01F867BFC0066283D /* OWSSystemMessageCell.m in Sources */,
45A663C51F92EC760027B59E /* GroupTableViewCell.swift in Sources */,
34D1F0861F8678AA0066283D /* ConversationViewController.m in Sources */,
@ -3106,11 +3114,7 @@
"DEBUG=1",
"$(inherited)",
);
"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = (
"DEBUG=1",
"$(inherited)",
"SSK_BUILDING_FOR_TESTS=1",
);
"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = "DEBUG=1 $(inherited) SSK_BUILDING_FOR_TESTS=1";
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

View File

@ -3,6 +3,7 @@
//
#import "DebugUIMisc.h"
#import "OWSBackup.h"
#import "OWSCountryMetadata.h"
#import "OWSTableViewController.h"
#import "RegistrationViewController.h"
@ -93,6 +94,10 @@ NS_ASSUME_NONNULL_BEGIN
[DebugUIMisc sendUnencryptedDatabase:thread];
}]];
}
[items addObject:[OWSTableItem itemWithTitle:@"Export Database"
actionBlock:^{
[OWSBackup exportDatabase];
}]];
return [OWSTableSection sectionWithTitle:self.name items:items];
}

View File

@ -0,0 +1,13 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
NS_ASSUME_NONNULL_BEGIN
@interface OWSBackup : NSObject
+ (void)exportDatabase;
@end
NS_ASSUME_NONNULL_END

230
Signal/src/util/OWSBackup.m Normal file
View File

@ -0,0 +1,230 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "OWSBackup.h"
#import "NSUserDefaults+OWS.h"
#import "zlib.h"
#import <SSZipArchive/SSZipArchive.h>
#import <SignalServiceKit/OWSFileSystem.h>
#import <SignalServiceKit/TSStorageManager.h>
NS_ASSUME_NONNULL_BEGIN
@interface OWSStorage (OWSBackup)
- (NSData *)databasePassword;
@end
#pragma mark -
@interface OWSBackup ()
@property (nonatomic) NSString *rootDirPath;
@end
#pragma mark -
@implementation OWSBackup
- (void)dealloc
{
OWSAssert(self.rootDirPath.length > 0);
DDLogInfo(@"%@ Cleaning up: %@", self.logTag, self.rootDirPath);
[OWSFileSystem deleteFile:self.rootDirPath];
}
+ (void)exportDatabase
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[OWSBackup new] exportDatabase];
});
}
- (void)exportDatabase
{
NSString *temporaryDirectory = NSTemporaryDirectory();
NSString *backupName = [NSUUID UUID].UUIDString;
NSString *rootDirPath = [temporaryDirectory stringByAppendingPathComponent:backupName];
self.rootDirPath = rootDirPath;
NSString *backupDirPath = [rootDirPath stringByAppendingPathComponent:@"Contents"];
NSString *backupZipPath = [rootDirPath stringByAppendingPathComponent:[backupName stringByAppendingString:@".zip"]];
DDLogInfo(@"%@ rootDirPath: %@", self.logTag, rootDirPath);
DDLogInfo(@"%@ backupDirPath: %@", self.logTag, backupDirPath);
DDLogInfo(@"%@ backupZipPath: %@", self.logTag, backupZipPath);
[OWSFileSystem ensureDirectoryExists:rootDirPath];
[OWSFileSystem protectFolderAtPath:rootDirPath];
[OWSFileSystem ensureDirectoryExists:backupDirPath];
NSData *databasePassword = [TSStorageManager sharedManager].databasePassword;
if (![self writeData:databasePassword fileName:@"databasePassword" backupDirPath:backupDirPath]) {
return;
}
if (![self writeUserDefaults:NSUserDefaults.standardUserDefaults
fileName:@"standardUserDefaults"
backupDirPath:backupDirPath]) {
return;
}
if (![self writeUserDefaults:NSUserDefaults.appUserDefaults
fileName:@"appUserDefaults"
backupDirPath:backupDirPath]) {
return;
}
if (![self copyDirectory:OWSFileSystem.appDocumentDirectoryPath
dstDirName:@"appDocumentDirectoryPath"
backupDirPath:backupDirPath]) {
return;
}
if (![self copyDirectory:OWSFileSystem.appSharedDataDirectoryPath
dstDirName:@"appSharedDataDirectoryPath"
backupDirPath:backupDirPath]) {
return;
}
if (![self zipDirectory:backupDirPath dstFilePath:backupZipPath]) {
return;
}
[OWSFileSystem protectFolderAtPath:backupZipPath];
}
- (BOOL)writeData:(NSData *)data fileName:(NSString *)fileName backupDirPath:(NSString *)backupDirPath
{
OWSAssert(data);
OWSAssert(fileName.length > 0);
OWSAssert(backupDirPath.length > 0);
NSString *filePath = [backupDirPath stringByAppendingPathComponent:fileName];
NSError *error;
BOOL success = [data writeToFile:filePath options:NSDataWritingAtomic error:&error];
if (!success || error) {
OWSFail(@"%@ failed to write user defaults: %@", self.logTag, error);
return NO;
}
return YES;
}
- (BOOL)copyDirectory:(NSString *)srcDirPath dstDirName:(NSString *)dstDirName backupDirPath:(NSString *)backupDirPath
{
OWSAssert(srcDirPath.length > 0);
OWSAssert(dstDirName.length > 0);
OWSAssert(backupDirPath.length > 0);
NSString *dstDirPath = [backupDirPath stringByAppendingPathComponent:dstDirName];
NSError *error = nil;
BOOL success = [[NSFileManager defaultManager] copyItemAtPath:srcDirPath toPath:dstDirPath error:&error];
if (!success || error) {
OWSFail(@"%@ failed to copy directory: %@, %@", self.logTag, dstDirName, error);
return NO;
}
return YES;
}
- (BOOL)writeUserDefaults:(NSUserDefaults *)userDefaults
fileName:(NSString *)fileName
backupDirPath:(NSString *)backupDirPath
{
OWSAssert(userDefaults);
OWSAssert(fileName.length > 0);
OWSAssert(backupDirPath.length > 0);
NSDictionary<NSString *, id> *dictionary = userDefaults.dictionaryRepresentation;
if (!dictionary) {
OWSFail(@"%@ failed to extract user defaults", self.logTag);
return NO;
}
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:dictionary];
if (!data) {
OWSFail(@"%@ failed to archive user defaults", self.logTag);
return NO;
}
return [self writeData:data fileName:fileName backupDirPath:backupDirPath];
}
- (BOOL)zipDirectory:(NSString *)srcDirPath dstFilePath:(NSString *)dstFilePath
{
OWSAssert(srcDirPath.length > 0);
OWSAssert(dstFilePath.length > 0);
// TODO:
NSString *password = [NSUUID UUID].UUIDString;
BOOL success = [SSZipArchive createZipFileAtPath:dstFilePath
withContentsOfDirectory:srcDirPath
keepParentDirectory:NO
compressionLevel:Z_DEFAULT_COMPRESSION
password:password
AES:YES
progressHandler:^(NSUInteger entryNumber, NSUInteger total) {
DDLogVerbose(@"%@ Zip progress: %zd / %zd = %f",
self.logTag,
entryNumber,
total,
entryNumber / (CGFloat)total);
// TODO:
}];
if (!success) {
OWSFail(@"%@ failed to write zip backup", self.logTag);
return NO;
}
NSError *error;
NSNumber *fileSize = [[NSFileManager defaultManager] attributesOfItemAtPath:dstFilePath error:&error][NSFileSize];
if (error) {
OWSFail(@"%@ failed to get zip file size: %@", self.logTag, error);
return NO;
}
DDLogVerbose(@"%@ Zip file size: %@", self.logTag, fileSize);
return YES;
}
//- (BOOL)zipDirectory:(NSString *)srcDirPath
// rootSrcDirPath:(NSString *)rootSrcDirPath
// zipFile:(OZZipFile *)zipFile
//{
// OWSAssert(srcDirPath.length > 0);
// OWSAssert(rootSrcDirPath.length > 0);
// OWSAssert(zipFile);
//
// NSFileManager *fileManager = [NSFileManager defaultManager] ;
// NSError *error;
// NSArray <NSString *> *filenames =[fileManager contentsOfDirectoryAtPath:srcDirPath error:&error];
// if (error) {
// OWSFail(@"%@ failed to get directory contents: %@", self.logTag, error);
// return NO;
// }
// for (NSString *fileName in filenames) {
//
// }
//
// // OZZipWriteStream *stream= [zipFile writeFileInZipWithName:@"abc.txt"
// // compressionLevel:OZZipCompressionLevelBest];
// //
// // [stream writeData:abcData];
// // [stream finishedWriting];}
// //
// // NSData *fileData= // Your file data
// // uint32_t crc= [fileData crc32];
// //
// // OZZipWriteStream *stream= [zipFile writeFileInZipWithName:@"abc.txt"
// // compressionLevel:OZZipCompressionLevelBest
// // password:@"password"
// // crc32:crc];
// //
// //[stream writeData:fileData];
// [stream finishedWriting];
//
// return YES;
//}
@end
NS_ASSUME_NONNULL_END