From 535c74ab1c972e026280c97901934b5057be35b6 Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Sun, 31 Mar 2019 08:46:54 +0100 Subject: [PATCH 01/17] Update conditions for SQLCipher validation Only expose SQLCipher validation code if we are using SQLCipher and not another encryption extension. --- GRDB/Core/Configuration.swift | 2 +- GRDB/Core/Database.swift | 6 +++--- GRDB/Core/DatabasePool.swift | 2 +- GRDB/Core/DatabaseQueue.swift | 2 +- GRDB/Core/DatabaseWriter.swift | 2 +- Tests/GRDBTests/EncryptionTests.swift | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/GRDB/Core/Configuration.swift b/GRDB/Core/Configuration.swift index 18934e3ffc..dde58d9c77 100644 --- a/GRDB/Core/Configuration.swift +++ b/GRDB/Core/Configuration.swift @@ -76,7 +76,7 @@ public struct Configuration { // MARK: - Encryption - #if SQLITE_HAS_CODEC + #if SQLITE_HAS_CODEC && GRDBCIPHER /// The passphrase for the encrypted database. /// /// Default: nil diff --git a/GRDB/Core/Database.swift b/GRDB/Core/Database.swift index cee17ca886..7d2c28022f 100644 --- a/GRDB/Core/Database.swift +++ b/GRDB/Core/Database.swift @@ -169,7 +169,7 @@ public final class Database { let sqliteConnection = try Database.openConnection(path: path, flags: configuration.SQLiteOpenFlags) do { try Database.activateExtendedCodes(sqliteConnection) - #if SQLITE_HAS_CODEC + #if SQLITE_HAS_CODEC && GRDBCIPHER try Database.validateSQLCipher(sqliteConnection) if let passphrase = configuration.passphrase { try Database.set(passphrase: passphrase, forConnection: sqliteConnection) @@ -229,7 +229,7 @@ extension Database { } } - #if SQLITE_HAS_CODEC + #if SQLITE_HAS_CODEC && GRDBCIPHER private static func validateSQLCipher(_ sqliteConnection: SQLiteConnection) throws { // https://discuss.zetetic.net/t/important-advisory-sqlcipher-with-xcode-8-and-new-sdks/1688 // @@ -914,7 +914,7 @@ extension Database { } } -#if SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC && GRDBCIPHER extension Database { // MARK: - Encryption diff --git a/GRDB/Core/DatabasePool.swift b/GRDB/Core/DatabasePool.swift index be673377ec..96dd71f7c4 100644 --- a/GRDB/Core/DatabasePool.swift +++ b/GRDB/Core/DatabasePool.swift @@ -215,7 +215,7 @@ extension DatabasePool { #endif } -#if SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC && GRDBCIPHER extension DatabasePool { // MARK: - Encryption diff --git a/GRDB/Core/DatabaseQueue.swift b/GRDB/Core/DatabaseQueue.swift index 7d87f698af..cfad83df0d 100644 --- a/GRDB/Core/DatabaseQueue.swift +++ b/GRDB/Core/DatabaseQueue.swift @@ -132,7 +132,7 @@ extension DatabaseQueue { #endif } -#if SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC && GRDBCIPHER extension DatabaseQueue { // MARK: - Encryption diff --git a/GRDB/Core/DatabaseWriter.swift b/GRDB/Core/DatabaseWriter.swift index c313ef2ba8..b915d5ad89 100644 --- a/GRDB/Core/DatabaseWriter.swift +++ b/GRDB/Core/DatabaseWriter.swift @@ -127,7 +127,7 @@ extension DatabaseWriter { /// - precondition: database is not accessed concurrently during the /// execution of this method. public func erase() throws { - #if SQLITE_HAS_CODEC + #if SQLITE_HAS_CODEC && GRDBCIPHER // SQLCipher does not support the backup API: https://discuss.zetetic.net/t/using-the-sqlite-online-backup-api/2631 // So we'll drop all database objects one after the other. try writeWithoutTransaction { db in diff --git a/Tests/GRDBTests/EncryptionTests.swift b/Tests/GRDBTests/EncryptionTests.swift index d96d4b16f8..5b17872429 100644 --- a/Tests/GRDBTests/EncryptionTests.swift +++ b/Tests/GRDBTests/EncryptionTests.swift @@ -1,4 +1,4 @@ -#if SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC && GRDBCIPHER import XCTest import GRDBCipher From c174af85d38d4dfa7eb900b09856f7dffe4311e4 Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Mon, 1 Apr 2019 21:29:19 +0100 Subject: [PATCH 02/17] Add support for SQLite Encryption Extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s all hidden behind a new flag called GRDB_SQLITE_SEE. This must be defined along with SQLITE_HAS_CODEC. --- GRDB/Core/Configuration.swift | 12 +++++++ GRDB/Core/Database.swift | 62 +++++++++++++++++++++++++++++++++++ GRDB/Core/DatabasePool.swift | 15 +++++++++ GRDB/Core/DatabaseQueue.swift | 11 +++++++ 4 files changed, 100 insertions(+) diff --git a/GRDB/Core/Configuration.swift b/GRDB/Core/Configuration.swift index dde58d9c77..3876569c2a 100644 --- a/GRDB/Core/Configuration.swift +++ b/GRDB/Core/Configuration.swift @@ -76,6 +76,7 @@ public struct Configuration { // MARK: - Encryption + // For SQLCipher #if SQLITE_HAS_CODEC && GRDBCIPHER /// The passphrase for the encrypted database. /// @@ -97,6 +98,17 @@ public struct Configuration { public var kdfIterations: Int = 64000 #endif + // For SQLite-SEE + #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + /// The key for the encrypted database. + /// + /// Default: nil + public var key: String? + + public var encryptionType: Database.EncryptionType = .AES128 + + #endif + // MARK: - Transactions diff --git a/GRDB/Core/Database.swift b/GRDB/Core/Database.swift index 7d2c28022f..34078431ad 100644 --- a/GRDB/Core/Database.swift +++ b/GRDB/Core/Database.swift @@ -177,6 +177,11 @@ public final class Database { try Database.set(kdfIterations: configuration.kdfIterations, forConnection: sqliteConnection) } #endif + #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + if let key = configuration.key { + try Database.set(key: key, withEncryptionType: configuration.encryptionType, forConnection: sqliteConnection) + } + #endif try Database.validateDatabaseFormat(sqliteConnection) } catch { Database.closeConnection(sqliteConnection) @@ -301,6 +306,20 @@ extension Database { } #endif + // For SQLite-SEE + #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + private static func set(key: String, withEncryptionType encType: EncryptionType, forConnection sqliteConnection: SQLiteConnection) throws { + let prefixedKey = "\(encType.prefixValue):\(key)" + let data = prefixedKey.data(using: .utf8)! + let code = data.withUnsafeBytes { + sqlite3_key_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) + } + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + } + #endif + private static func validateDatabaseFormat(_ sqliteConnection: SQLiteConnection) throws { // Users are surprised when they open a picture as a database and // see no error (https://github.com/groue/GRDB.swift/issues/54). @@ -946,6 +965,24 @@ extension Database { } #endif +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE +extension Database { + + // MARK: - Encryption + + func change(key: String, encryptionType:EncryptionType) throws { + let prefixedKey = "\(encryptionType.prefixValue):\(key)" + let data = prefixedKey.data(using: .utf8)! + let code = data.withUnsafeBytes { + sqlite3_rekey_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) + } + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: lastErrorMessage) + } + } +} +#endif + extension Database { /// See BusyMode and https://www.sqlite.org/c3ref/busy_handler.html @@ -1128,3 +1165,28 @@ extension Database { } } } + +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE +extension Database { + /// An SQLite Encryption Extension encryption type + /// + /// The raw value is the string to prefix a key with + public enum EncryptionType: String { + + /// - note: AES128 is the default choice for new files + case AES128 + + + case AES256 + + /// - warning: Use of RC4 for new files is not recommended + case RC4 + + + /// Prefix key strings with this value to specify type + public var prefixValue: String { + return String(describing: self).lowercased() + } + } +} +#endif diff --git a/GRDB/Core/DatabasePool.swift b/GRDB/Core/DatabasePool.swift index 96dd71f7c4..ab40bd746a 100644 --- a/GRDB/Core/DatabasePool.swift +++ b/GRDB/Core/DatabasePool.swift @@ -229,6 +229,21 @@ extension DatabasePool { } } #endif +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + extension DatabasePool { + + // MARK: - Encryption + + /// Changes the passphrase of an encrypted database + public func change(passphrase: String, encryptionType: Database.EncryptionType) throws { + try readerPool.clear(andThen: { + try writer.sync { try $0.change(key: passphrase, encryptionType: encryptionType) } + readerConfig.key = passphrase + readerConfig.encryptionType = encryptionType + }) + } + } +#endif extension DatabasePool : DatabaseReader { diff --git a/GRDB/Core/DatabaseQueue.swift b/GRDB/Core/DatabaseQueue.swift index cfad83df0d..afc3339b55 100644 --- a/GRDB/Core/DatabaseQueue.swift +++ b/GRDB/Core/DatabaseQueue.swift @@ -143,6 +143,17 @@ extension DatabaseQueue { } } #endif +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE +extension DatabaseQueue { + + // MARK: - Encryption + + /// Changes the passphrase of an encrypted database + public func change(passphrase: String, encryptionType: Database.EncryptionType) throws { + try writer.sync { try $0.change(key: passphrase, encryptionType: encryptionType) } + } +} +#endif extension DatabaseQueue { From 22bbc1f800f3f47327f13f59ddbdd2ead6f52880 Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Mon, 1 Apr 2019 21:30:30 +0100 Subject: [PATCH 03/17] Add tests to check new encryption code Also renamed existing SQLCipher tests to make distinction clear. --- GRDBCustom.xcodeproj/project.pbxproj | 18 +- ...ptionTests.swift => GRDBCipherTests.swift} | 2 +- .../SQLiteEncryptionExtensionTests.swift | 429 ++++++++++++++++++ 3 files changed, 442 insertions(+), 7 deletions(-) rename Tests/GRDBTests/{EncryptionTests.swift => GRDBCipherTests.swift} (99%) create mode 100644 Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift diff --git a/GRDBCustom.xcodeproj/project.pbxproj b/GRDBCustom.xcodeproj/project.pbxproj index d6af4d6ec3..8fb83b2dfb 100755 --- a/GRDBCustom.xcodeproj/project.pbxproj +++ b/GRDBCustom.xcodeproj/project.pbxproj @@ -42,8 +42,8 @@ 5616AAF7207CD59400AC3664 /* RequestProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5616AAF4207CD59300AC3664 /* RequestProtocols.swift */; }; 561729552235340E0006E219 /* EncodableRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 561729532235340E0006E219 /* EncodableRecord.swift */; }; 561729562235340E0006E219 /* EncodableRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 561729532235340E0006E219 /* EncodableRecord.swift */; }; - 56176C7E1EACCD2F000F3F2B /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 56176C801EACCD31000F3F2B /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; + 56176C7E1EACCD2F000F3F2B /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* GRDBCipherTests.swift */; }; + 56176C801EACCD31000F3F2B /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* GRDBCipherTests.swift */; }; 562205FA1E420E49005860AC /* DatabasePoolReleaseMemoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563363CF1C943D13000BE133 /* DatabasePoolReleaseMemoryTests.swift */; }; 562205FB1E420E49005860AC /* DatabasePoolSchemaCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569531281C908A5B00CF1A2B /* DatabasePoolSchemaCacheTests.swift */; }; 562205FC1E420E49005860AC /* DatabaseQueueReleaseMemoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563363D41C94484E000BE133 /* DatabaseQueueReleaseMemoryTests.swift */; }; @@ -428,6 +428,8 @@ 56FF455D1D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */; }; 6340BF831E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; 6340BF871E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; + B5C0503F2252574500CA794A /* SQLiteEncryptionExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C0503E2252574500CA794A /* SQLiteEncryptionExtensionTests.swift */; }; + B5C050402252574500CA794A /* SQLiteEncryptionExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C0503E2252574500CA794A /* SQLiteEncryptionExtensionTests.swift */; }; EED476F21CFD17270026A4EC /* GRDBCustomSQLite-USER.h in Headers */ = {isa = PBXBuildFile; fileRef = EED476F11CFD16FF0026A4EC /* GRDBCustomSQLite-USER.h */; settings = {ATTRIBUTES = (Public, ); }; }; EED476F31CFD172C0026A4EC /* GRDBCustomSQLite-USER.h in Headers */ = {isa = PBXBuildFile; fileRef = EED476F11CFD16FF0026A4EC /* GRDBCustomSQLite-USER.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3BA800A1CFB286A003DC1BA /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A238701B9C75030082EB20 /* Configuration.swift */; }; @@ -824,7 +826,7 @@ 56703299212B5461007D270F /* DatabaseUUIDEncodingStrategyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseUUIDEncodingStrategyTests.swift; sourceTree = ""; }; 567071F2208A00BE006AD95A /* SQLiteDateParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteDateParser.swift; sourceTree = ""; }; 567156151CB142AA007DC145 /* DatabaseQueueReadOnlyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueReadOnlyTests.swift; sourceTree = ""; }; - 567156701CB18050007DC145 /* EncryptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionTests.swift; sourceTree = ""; }; + 567156701CB18050007DC145 /* GRDBCipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GRDBCipherTests.swift; sourceTree = ""; }; 5671FC1F1DA3CAC9003BF4FF /* FTS3TokenizerDescriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS3TokenizerDescriptor.swift; sourceTree = ""; }; 5672DE581CDB72520022BA81 /* DatabaseQueueBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueBackupTests.swift; sourceTree = ""; }; 5672DE661CDB751D0022BA81 /* DatabasePoolBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabasePoolBackupTests.swift; sourceTree = ""; }; @@ -979,6 +981,7 @@ 56FF453F1D2C23BA00F21EF9 /* MutablePersistableRecordDeleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutablePersistableRecordDeleteTests.swift; sourceTree = ""; }; 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordUniqueIndexTests.swift; sourceTree = ""; }; 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordPersistenceConflictPolicy.swift; sourceTree = ""; }; + B5C0503E2252574500CA794A /* SQLiteEncryptionExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteEncryptionExtensionTests.swift; sourceTree = ""; }; DC2393C61ABE35F8003FF113 /* GRDB-Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GRDB-Bridging.h"; sourceTree = ""; }; DC3773F719C8CBB3004FCF85 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DC3773F819C8CBB3004FCF85 /* GRDB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GRDB.h; sourceTree = ""; }; @@ -1211,7 +1214,8 @@ 56176CA01EACEE2A000F3F2B /* GRDBCipher */ = { isa = PBXGroup; children = ( - 567156701CB18050007DC145 /* EncryptionTests.swift */, + 567156701CB18050007DC145 /* GRDBCipherTests.swift */, + B5C0503E2252574500CA794A /* SQLiteEncryptionExtensionTests.swift */, ); name = GRDBCipher; sourceTree = ""; @@ -2150,6 +2154,7 @@ 5653EB7D20961FB200F46237 /* AssociationBelongsToSQLTests.swift in Sources */, 563B070021861D9D00B38F35 /* ValueObservationCountTests.swift in Sources */, 5698AC0A1D9B9FCF0056AF8C /* QueryInterfaceExtensibilityTests.swift in Sources */, + B5C050402252574500CA794A /* SQLiteEncryptionExtensionTests.swift in Sources */, 56EA63CA209C7F1E009715B8 /* DerivableRequestTests.swift in Sources */, F3BA80E91CFB3016003DC1BA /* RowAdapterTests.swift in Sources */, 562393371DEDFC5700A6B01F /* AnyCursorTests.swift in Sources */, @@ -2196,7 +2201,7 @@ 563B071321862C3E00B38F35 /* ValueObservationRecordTests.swift in Sources */, 56B6EF61208CB746002F0ACB /* ColumnExpressionTests.swift in Sources */, F3BA81121CFB3059003DC1BA /* DatabaseMigratorTests.swift in Sources */, - 56176C801EACCD31000F3F2B /* EncryptionTests.swift in Sources */, + 56176C801EACCD31000F3F2B /* GRDBCipherTests.swift in Sources */, F3BA80EB1CFB3016003DC1BA /* FetchableRecordTests.swift in Sources */, 56B86E71220FF4C900524C16 /* SQLLiteralTests.swift in Sources */, F3BA81151CFB305E003DC1BA /* Record+QueryInterfaceRequestTests.swift in Sources */, @@ -2467,6 +2472,7 @@ 5653EB7C20961FB200F46237 /* AssociationBelongsToSQLTests.swift in Sources */, 563B06FF21861D9D00B38F35 /* ValueObservationCountTests.swift in Sources */, 5698ACDA1DA925430056AF8C /* RowTestCase.swift in Sources */, + B5C0503F2252574500CA794A /* SQLiteEncryptionExtensionTests.swift in Sources */, 56EA63C9209C7F1E009715B8 /* DerivableRequestTests.swift in Sources */, F3BA811D1CFB305F003DC1BA /* TableRecord+QueryInterfaceRequestTests.swift in Sources */, F3BA80AD1CFB2FA6003DC1BA /* DataMemoryTests.swift in Sources */, @@ -2514,7 +2520,7 @@ 56B6EF60208CB746002F0ACB /* ColumnExpressionTests.swift in Sources */, 562205FD1E420EA2005860AC /* DatabasePoolBackupTests.swift in Sources */, 562206091E420EB2005860AC /* DatabaseQueueConcurrencyTests.swift in Sources */, - 56176C7E1EACCD2F000F3F2B /* EncryptionTests.swift in Sources */, + 56176C7E1EACCD2F000F3F2B /* GRDBCipherTests.swift in Sources */, 56B86E70220FF4C900524C16 /* SQLLiteralTests.swift in Sources */, F3BA811C1CFB305F003DC1BA /* QueryInterfaceExpressionsTests.swift in Sources */, 5698ACD11DA8C2620056AF8C /* RecordPrimaryKeyHiddenRowIDTests.swift in Sources */, diff --git a/Tests/GRDBTests/EncryptionTests.swift b/Tests/GRDBTests/GRDBCipherTests.swift similarity index 99% rename from Tests/GRDBTests/EncryptionTests.swift rename to Tests/GRDBTests/GRDBCipherTests.swift index 5b17872429..8e398666a3 100644 --- a/Tests/GRDBTests/EncryptionTests.swift +++ b/Tests/GRDBTests/GRDBCipherTests.swift @@ -2,7 +2,7 @@ import XCTest import GRDBCipher -class EncryptionTests: GRDBTestCase { +class GRDBEncryptionTests: GRDBTestCase { func testDatabaseQueueWithPassphraseToDatabaseQueueWithPassphrase() throws { do { diff --git a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift new file mode 100644 index 0000000000..47c1103804 --- /dev/null +++ b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift @@ -0,0 +1,429 @@ +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE +import XCTest +@testable import GRDBCustomSQLite + +class GRDBSQLiteSEETests: GRDBTestCase { + + func testDatabaseQueueWithKeyToDatabaseQueueWithKey() throws { + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithoutKey() throws { + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = nil + do { + _ = try makeDatabaseQueue(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithWrongKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "wrong" + do { + _ = try makeDatabaseQueue(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithWrongEncryptionType() throws { + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES256 + do { + _ = try makeDatabaseQueue(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithNewKeyAndType() throws { + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.change(passphrase: "newSecret", encryptionType: .AES256) + try dbQueue.inDatabase { db in + try db.execute(sql: "INSERT INTO data (value) VALUES (2)") + } + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + + do { + dbConfiguration.key = "newSecret" + dbConfiguration.encryptionType = .AES256 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + } + + func testDatabaseQueueWithKeyToDatabasePoolWithKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.read { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + + func testDatabaseQueueWithKeyToDatabasePoolWithoutKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = nil + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabasePoolWithWrongKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "wrong" + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabasePoolWithNewKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.change(passphrase: "newSecret", encryptionType: .AES128) + try dbPool.write { db in + try db.execute(sql: "INSERT INTO data (value) VALUES (2)") + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + + do { + dbConfiguration.key = "newSecret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.read { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithKey() throws { + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.read { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithoutKey() throws { + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = nil + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithWrongKey() throws { + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "wrong" + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithWrongEncryptionType() throws { + let key = "secret" + do { + dbConfiguration.key = key + dbConfiguration.encryptionType = .AES128 + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = key + dbConfiguration.encryptionType = .AES256 + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithNewKey() throws { + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.change(passphrase: "newSecret", encryptionType: .AES128) + try dbPool.write { db in + try db.execute(sql: "INSERT INTO data (value) VALUES (2)") + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + + do { + dbConfiguration.key = "newSecret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.read { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + } + + func testDatabaseQueueWithPragmaKeyToDatabaseQueueWithKey() throws { + do { + dbConfiguration.key = nil + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "PRAGMA key = 'secret'") + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + + func testDatabaseQueueWithPragmaKeyToDatabaseQueueWithoutKey() throws { + do { + dbConfiguration.key = nil + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "PRAGMA key = 'secret'") + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = nil + do { + _ = try makeDatabaseQueue(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithPragmaRemovedKeyAndType() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "PRAGMA rekey = ''") + } + } + + do { + dbConfiguration.key = nil + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + +} +#endif From 02385ea4ac927c8eb33fab5aa1303785d3dc4c5f Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Wed, 3 Apr 2019 13:58:41 +0100 Subject: [PATCH 04/17] Add renamed tests to other targets Missing from last commit --- GRDB.xcodeproj/project.pbxproj | 13 +++++++------ GRDBCipher.xcodeproj/project.pbxproj | 17 +++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/GRDB.xcodeproj/project.pbxproj b/GRDB.xcodeproj/project.pbxproj index fa5ed150e2..7fdd6495ff 100755 --- a/GRDB.xcodeproj/project.pbxproj +++ b/GRDB.xcodeproj/project.pbxproj @@ -97,8 +97,6 @@ 56176C6E1EACCCC9000F3F2B /* FTS5TableBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B964C21DA521450002DA19 /* FTS5TableBuilderTests.swift */; }; 56176C6F1EACCCC9000F3F2B /* FTS5TokenizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5698ACCA1DA62A2D0056AF8C /* FTS5TokenizerTests.swift */; }; 56176C701EACCCC9000F3F2B /* FTS5WrapperTokenizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ED8A7E1DAB8D6800BD0ABC /* FTS5WrapperTokenizerTests.swift */; }; - 56176C7D1EACCD2D000F3F2B /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 56176C7F1EACCD2F000F3F2B /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; 56193E8E1CD8A3E200F95862 /* FetchedRecordsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A7787C1C6A4DD600F507F6 /* FetchedRecordsController.swift */; }; 562205F11E420E47005860AC /* DatabasePoolReleaseMemoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563363CF1C943D13000BE133 /* DatabasePoolReleaseMemoryTests.swift */; }; 562205F21E420E47005860AC /* DatabasePoolSchemaCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569531281C908A5B00CF1A2B /* DatabasePoolSchemaCacheTests.swift */; }; @@ -797,6 +795,8 @@ 56FF455A1D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */; }; 6340BF801E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; 6340BF841E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; + B501B3482254A5440071DCDC /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501B3472254A5440071DCDC /* GRDBCipherTests.swift */; }; + B501B3492254A5440071DCDC /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501B3472254A5440071DCDC /* GRDBCipherTests.swift */; }; C96C0F2B2084A442006B2981 /* SQLiteDateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C96C0F242084A442006B2981 /* SQLiteDateParser.swift */; }; C96C0F2C2084A459006B2981 /* SQLiteDateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C96C0F242084A442006B2981 /* SQLiteDateParser.swift */; }; C96C0F2D2084A45A006B2981 /* SQLiteDateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C96C0F242084A442006B2981 /* SQLiteDateParser.swift */; }; @@ -1072,7 +1072,6 @@ 56703290212B544F007D270F /* DatabaseUUIDEncodingStrategyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseUUIDEncodingStrategyTests.swift; sourceTree = ""; }; 567071FA208A509C006AD95A /* DateParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateParsingTests.swift; sourceTree = ""; }; 567156151CB142AA007DC145 /* DatabaseQueueReadOnlyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueReadOnlyTests.swift; sourceTree = ""; }; - 567156701CB18050007DC145 /* EncryptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionTests.swift; sourceTree = ""; }; 5671FC1F1DA3CAC9003BF4FF /* FTS3TokenizerDescriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS3TokenizerDescriptor.swift; sourceTree = ""; }; 5672DE581CDB72520022BA81 /* DatabaseQueueBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueBackupTests.swift; sourceTree = ""; }; 5672DE661CDB751D0022BA81 /* DatabasePoolBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabasePoolBackupTests.swift; sourceTree = ""; }; @@ -1268,6 +1267,7 @@ 56FF453F1D2C23BA00F21EF9 /* MutablePersistableRecordDeleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutablePersistableRecordDeleteTests.swift; sourceTree = ""; }; 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordUniqueIndexTests.swift; sourceTree = ""; }; 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordPersistenceConflictPolicy.swift; sourceTree = ""; }; + B501B3472254A5440071DCDC /* GRDBCipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GRDBCipherTests.swift; sourceTree = ""; }; C96C0F242084A442006B2981 /* SQLiteDateParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteDateParser.swift; sourceTree = ""; }; DC2393C61ABE35F8003FF113 /* GRDB-Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GRDB-Bridging.h"; sourceTree = ""; }; DC3773F319C8CBB3004FCF85 /* GRDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GRDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1507,7 +1507,7 @@ 56176CA01EACEE2A000F3F2B /* GRDBCipher */ = { isa = PBXGroup; children = ( - 567156701CB18050007DC145 /* EncryptionTests.swift */, + B501B3472254A5440071DCDC /* GRDBCipherTests.swift */, ); name = GRDBCipher; sourceTree = ""; @@ -2318,6 +2318,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -2843,6 +2844,7 @@ 5657AB421D108BA9006283EF /* FoundationNSDataTests.swift in Sources */, 56CC922D201DFFB900CB597E /* DropWhileCursorTests.swift in Sources */, 56D507621F6BAE8600AE1C5B /* PrimaryKeyInfoTests.swift in Sources */, + B501B3492254A5440071DCDC /* GRDBCipherTests.swift in Sources */, 5653EADD20944B4F00F46237 /* AssociationBelongsToSQLTests.swift in Sources */, 56176C6B1EACCCC9000F3F2B /* FTS5CustomTokenizerTests.swift in Sources */, 56300B621C53C42C005A543B /* FetchableRecord+QueryInterfaceRequestTests.swift in Sources */, @@ -2859,7 +2861,6 @@ 56EB0AB31BCD787300A3DC55 /* DataMemoryTests.swift in Sources */, 56B6EF57208CB4E3002F0ACB /* ColumnExpressionTests.swift in Sources */, 563B071621862C4700B38F35 /* ValueObservationRecordTests.swift in Sources */, - 56176C7F1EACCD2F000F3F2B /* EncryptionTests.swift in Sources */, 56741EAC1E66A8B3003E422D /* FetchRequestTests.swift in Sources */, 5672DE5C1CDB72520022BA81 /* DatabaseQueueBackupTests.swift in Sources */, 56A2385E1B9C74A90082EB20 /* RecordCopyTests.swift in Sources */, @@ -3032,6 +3033,7 @@ 56CC9251201E093F00CB597E /* PrefixCursorTests.swift in Sources */, 56D4965E1D81304E008276D7 /* FoundationNSStringTests.swift in Sources */, 5698AC891DA389380056AF8C /* FTS3TableBuilderTests.swift in Sources */, + B501B3482254A5440071DCDC /* GRDBCipherTests.swift in Sources */, 56CC922C201DFFB900CB597E /* DropWhileCursorTests.swift in Sources */, 5653EADC20944B4F00F46237 /* AssociationBelongsToSQLTests.swift in Sources */, 56D5075E1F6BAE8600AE1C5B /* PrimaryKeyInfoTests.swift in Sources */, @@ -3050,7 +3052,6 @@ 563B071521862C4700B38F35 /* ValueObservationRecordTests.swift in Sources */, 56B6EF56208CB4E3002F0ACB /* ColumnExpressionTests.swift in Sources */, 5698ACD71DA925420056AF8C /* RowTestCase.swift in Sources */, - 56176C7D1EACCD2D000F3F2B /* EncryptionTests.swift in Sources */, 56741EA81E66A8B3003E422D /* FetchRequestTests.swift in Sources */, 56B86E79220FF4E000524C16 /* SQLLiteralTests.swift in Sources */, 56D496831D813147008276D7 /* DatabaseSavepointTests.swift in Sources */, diff --git a/GRDBCipher.xcodeproj/project.pbxproj b/GRDBCipher.xcodeproj/project.pbxproj index cdc5ba54ac..6575b8dad0 100755 --- a/GRDBCipher.xcodeproj/project.pbxproj +++ b/GRDBCipher.xcodeproj/project.pbxproj @@ -591,10 +591,6 @@ 567DAF371EAB789800FC0928 /* DatabaseLogErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567DAF341EAB789800FC0928 /* DatabaseLogErrorTests.swift */; }; 567DAF3A1EAB789800FC0928 /* DatabaseLogErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567DAF341EAB789800FC0928 /* DatabaseLogErrorTests.swift */; }; 567DAF3B1EAB789800FC0928 /* DatabaseLogErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567DAF341EAB789800FC0928 /* DatabaseLogErrorTests.swift */; }; - 567E55ED1D2BDD3D00CC6F79 /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 567E55EE1D2BDD3F00CC6F79 /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 567E55F31D2BDDFE00CC6F79 /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 567E55F41D2BDDFF00CC6F79 /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; 567F0B33220F196F00D111FB /* SQLInterpolationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567F0B32220F196F00D111FB /* SQLInterpolationTests.swift */; }; 567F0B34220F196F00D111FB /* SQLInterpolationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567F0B32220F196F00D111FB /* SQLInterpolationTests.swift */; }; 567F0B35220F196F00D111FB /* SQLInterpolationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567F0B32220F196F00D111FB /* SQLInterpolationTests.swift */; }; @@ -995,6 +991,8 @@ 6340BF821E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; 6340BF851E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; 6340BF861E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; + B501B34C2254A56B0071DCDC /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501B34B2254A56B0071DCDC /* GRDBCipherTests.swift */; }; + B501B34D2254A56B0071DCDC /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501B34B2254A56B0071DCDC /* GRDBCipherTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1208,7 +1206,6 @@ 567071F6208A00D4006AD95A /* SQLiteDateParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteDateParser.swift; sourceTree = ""; }; 567156151CB142AA007DC145 /* DatabaseQueueReadOnlyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueReadOnlyTests.swift; sourceTree = ""; }; 5671566C1CB16729007DC145 /* GRDBCipherOSXEncryptedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GRDBCipherOSXEncryptedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 567156701CB18050007DC145 /* EncryptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionTests.swift; sourceTree = ""; }; 5671FC1F1DA3CAC9003BF4FF /* FTS3TokenizerDescriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS3TokenizerDescriptor.swift; sourceTree = ""; }; 5672DE581CDB72520022BA81 /* DatabaseQueueBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueBackupTests.swift; sourceTree = ""; }; 5672DE661CDB751D0022BA81 /* DatabasePoolBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabasePoolBackupTests.swift; sourceTree = ""; }; @@ -1371,6 +1368,7 @@ 56FF453F1D2C23BA00F21EF9 /* MutablePersistableRecordDeleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutablePersistableRecordDeleteTests.swift; sourceTree = ""; }; 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordUniqueIndexTests.swift; sourceTree = ""; }; 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordPersistenceConflictPolicy.swift; sourceTree = ""; }; + B501B34B2254A56B0071DCDC /* GRDBCipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GRDBCipherTests.swift; sourceTree = ""; }; DC2393C61ABE35F8003FF113 /* GRDB-Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GRDB-Bridging.h"; sourceTree = ""; }; DC3773F719C8CBB3004FCF85 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DC3773F819C8CBB3004FCF85 /* GRDB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GRDB.h; sourceTree = ""; }; @@ -1621,7 +1619,7 @@ 56176CA01EACEE2A000F3F2B /* GRDBCipher */ = { isa = PBXGroup; children = ( - 567156701CB18050007DC145 /* EncryptionTests.swift */, + B501B34B2254A56B0071DCDC /* GRDBCipherTests.swift */, ); name = GRDBCipher; sourceTree = ""; @@ -2285,6 +2283,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -2559,6 +2558,7 @@ 560FC5791CB00B880014AA8E /* RecordPrimaryKeyMultipleTests.swift in Sources */, 56CC9257201E094F00CB597E /* PrefixCursorTests.swift in Sources */, 5698AC971DA4B0430056AF8C /* FTS4RecordTests.swift in Sources */, + B501B34C2254A56B0071DCDC /* GRDBCipherTests.swift in Sources */, 5657AB4F1D108BA9006283EF /* FoundationNSNumberTests.swift in Sources */, 56CC9236201E009700CB597E /* DropWhileCursorTests.swift in Sources */, 56D5075F1F6BAE8600AE1C5B /* PrimaryKeyInfoTests.swift in Sources */, @@ -2593,7 +2593,6 @@ 5665F86C203EF47D0084C6C0 /* ColumnInfoTests.swift in Sources */, 560FC5811CB00B880014AA8E /* RecordCopyTests.swift in Sources */, 560FC5821CB00B880014AA8E /* RawRepresentable+DatabaseValueConvertibleTests.swift in Sources */, - 567E55ED1D2BDD3D00CC6F79 /* EncryptionTests.swift in Sources */, 56741EA91E66A8B3003E422D /* FetchRequestTests.swift in Sources */, 56176C641EACCCC7000F3F2B /* FTS5WrapperTokenizerTests.swift in Sources */, 5653EBB820961FE800F46237 /* AssociationParallelSQLTests.swift in Sources */, @@ -2767,7 +2766,6 @@ 56B6EF65208CB762002F0ACB /* ColumnExpressionTests.swift in Sources */, 5671563C1CB16729007DC145 /* MutablePersistableRecordTests.swift in Sources */, 5671563D1CB16729007DC145 /* FetchableRecord+QueryInterfaceRequestTests.swift in Sources */, - 567E55F31D2BDDFE00CC6F79 /* EncryptionTests.swift in Sources */, 56B86E7E220FF4F500524C16 /* SQLLiteralTests.swift in Sources */, 5671563F1CB16729007DC145 /* RecordCopyTests.swift in Sources */, 5698ACB81DA6285E0056AF8C /* FTS3TokenizerTests.swift in Sources */, @@ -3024,7 +3022,6 @@ 562EA82B1F17B2AC00FA528C /* CompilationProtocolTests.swift in Sources */, 56DAA2D71DE99DAB006E10C8 /* DatabaseCursorTests.swift in Sources */, 5698AC4E1DA2D48A0056AF8C /* FTS3RecordTests.swift in Sources */, - 567E55EE1D2BDD3F00CC6F79 /* EncryptionTests.swift in Sources */, 56AFCA361CB1AA9900F48B96 /* DatabaseCollationTests.swift in Sources */, 56AFCA371CB1AA9900F48B96 /* StatementColumnConvertibleFetchTests.swift in Sources */, 56AFCA381CB1AA9900F48B96 /* QueryInterfaceExpressionsTests.swift in Sources */, @@ -3065,6 +3062,7 @@ 566AD8CB1D531BED002EC1A8 /* TableDefinitionTests.swift in Sources */, 56AFCA4B1CB1AA9900F48B96 /* MutablePersistableRecordTests.swift in Sources */, 56CC9259201E094F00CB597E /* PrefixCursorTests.swift in Sources */, + B501B34D2254A56B0071DCDC /* GRDBCipherTests.swift in Sources */, 5657AB431D108BA9006283EF /* FoundationNSDataTests.swift in Sources */, 56AFCA4C1CB1AA9900F48B96 /* FetchableRecord+QueryInterfaceRequestTests.swift in Sources */, 56CC9238201E009900CB597E /* DropWhileCursorTests.swift in Sources */, @@ -3214,7 +3212,6 @@ 56DAA2D81DE99DAB006E10C8 /* DatabaseCursorTests.swift in Sources */, 5698AC4F1DA2D48A0056AF8C /* FTS3RecordTests.swift in Sources */, 56AFCA8F1CB1ABC800F48B96 /* DatabaseCollationTests.swift in Sources */, - 567E55F41D2BDDFF00CC6F79 /* EncryptionTests.swift in Sources */, 56AFCA901CB1ABC800F48B96 /* StatementColumnConvertibleFetchTests.swift in Sources */, 5690C33D1D23E7D200E59934 /* FoundationDateTests.swift in Sources */, 5672DE6C1CDB751D0022BA81 /* DatabasePoolBackupTests.swift in Sources */, From 22fde350154d97703c35857fa2adccea3251880d Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Wed, 3 Apr 2019 20:58:08 +0100 Subject: [PATCH 05/17] Renaming things for consistency EncryptionType -> Algorithm passphrase -> key Tidy enum values --- GRDB/Core/Configuration.swift | 10 +++++-- GRDB/Core/Database.swift | 24 ++++++--------- GRDB/Core/DatabasePool.swift | 10 +++---- GRDB/Core/DatabaseQueue.swift | 6 ++-- .../SQLiteEncryptionExtensionTests.swift | 30 +++++++++---------- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/GRDB/Core/Configuration.swift b/GRDB/Core/Configuration.swift index 3876569c2a..96bf1da55a 100644 --- a/GRDB/Core/Configuration.swift +++ b/GRDB/Core/Configuration.swift @@ -100,12 +100,18 @@ public struct Configuration { // For SQLite-SEE #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE - /// The key for the encrypted database. + + /// The key for an attempting to open an encrypted database. + /// + /// This is ignored if set to nil /// /// Default: nil public var key: String? - public var encryptionType: Database.EncryptionType = .AES128 + /// Which algorithm to use when opening an encrypted database + /// + /// This is only used if a key is set to non-nil + public var encryptionAlgorithm: Database.EncryptionAlgorithm = .AES128 #endif diff --git a/GRDB/Core/Database.swift b/GRDB/Core/Database.swift index 34078431ad..ce2b6ad69a 100644 --- a/GRDB/Core/Database.swift +++ b/GRDB/Core/Database.swift @@ -179,7 +179,7 @@ public final class Database { #endif #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE if let key = configuration.key { - try Database.set(key: key, withEncryptionType: configuration.encryptionType, forConnection: sqliteConnection) + try Database.set(key: key, withEncryptionAlgorithm: configuration.encryptionAlgorithm, forConnection: sqliteConnection) } #endif try Database.validateDatabaseFormat(sqliteConnection) @@ -308,8 +308,8 @@ extension Database { // For SQLite-SEE #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE - private static func set(key: String, withEncryptionType encType: EncryptionType, forConnection sqliteConnection: SQLiteConnection) throws { - let prefixedKey = "\(encType.prefixValue):\(key)" + private static func set(key: String, withEncryptionAlgorithm algorithm: EncryptionAlgorithm, forConnection sqliteConnection: SQLiteConnection) throws { + let prefixedKey = "\(algorithm.rawValue):\(key)" let data = prefixedKey.data(using: .utf8)! let code = data.withUnsafeBytes { sqlite3_key_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) @@ -970,8 +970,8 @@ extension Database { // MARK: - Encryption - func change(key: String, encryptionType:EncryptionType) throws { - let prefixedKey = "\(encryptionType.prefixValue):\(key)" + func change(key: String, encryptionAlgorithm:EncryptionAlgorithm) throws { + let prefixedKey = "\(encryptionAlgorithm.rawValue):\(key)" let data = prefixedKey.data(using: .utf8)! let code = data.withUnsafeBytes { sqlite3_rekey_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) @@ -1171,22 +1171,16 @@ extension Database { /// An SQLite Encryption Extension encryption type /// /// The raw value is the string to prefix a key with - public enum EncryptionType: String { + public enum EncryptionAlgorithm: String { /// - note: AES128 is the default choice for new files - case AES128 + case AES128 = "aes128" - case AES256 + case AES256 = "aes256" /// - warning: Use of RC4 for new files is not recommended - case RC4 - - - /// Prefix key strings with this value to specify type - public var prefixValue: String { - return String(describing: self).lowercased() - } + case RC4 = "rc4" } } #endif diff --git a/GRDB/Core/DatabasePool.swift b/GRDB/Core/DatabasePool.swift index ab40bd746a..bd5cf15ac2 100644 --- a/GRDB/Core/DatabasePool.swift +++ b/GRDB/Core/DatabasePool.swift @@ -234,12 +234,12 @@ extension DatabasePool { // MARK: - Encryption - /// Changes the passphrase of an encrypted database - public func change(passphrase: String, encryptionType: Database.EncryptionType) throws { + /// Changes the key of an encrypted database + public func change(key: String, encryptionAlgorithm: Database.EncryptionAlgorithm) throws { try readerPool.clear(andThen: { - try writer.sync { try $0.change(key: passphrase, encryptionType: encryptionType) } - readerConfig.key = passphrase - readerConfig.encryptionType = encryptionType + try writer.sync { try $0.change(key: key, encryptionAlgorithm: encryptionAlgorithm) } + readerConfig.key = key + readerConfig.encryptionAlgorithm = encryptionAlgorithm }) } } diff --git a/GRDB/Core/DatabaseQueue.swift b/GRDB/Core/DatabaseQueue.swift index afc3339b55..e3c8641223 100644 --- a/GRDB/Core/DatabaseQueue.swift +++ b/GRDB/Core/DatabaseQueue.swift @@ -148,9 +148,9 @@ extension DatabaseQueue { // MARK: - Encryption - /// Changes the passphrase of an encrypted database - public func change(passphrase: String, encryptionType: Database.EncryptionType) throws { - try writer.sync { try $0.change(key: passphrase, encryptionType: encryptionType) } + /// Changes the key of an encrypted database + public func change(key: String, encryptionAlgorithm: Database.EncryptionAlgorithm) throws { + try writer.sync { try $0.change(key: key, encryptionAlgorithm: encryptionAlgorithm) } } } #endif diff --git a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift index 47c1103804..25cdd92322 100644 --- a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift +++ b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift @@ -7,7 +7,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { func testDatabaseQueueWithKeyToDatabaseQueueWithKey() throws { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -17,7 +17,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) @@ -28,7 +28,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { func testDatabaseQueueWithKeyToDatabaseQueueWithoutKey() throws { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -74,10 +74,10 @@ class GRDBSQLiteSEETests: GRDBTestCase { } } - func testDatabaseQueueWithKeyToDatabaseQueueWithWrongEncryptionType() throws { + func testDatabaseQueueWithKeyToDatabaseQueueWithWrongEncryptionAlgorithm() throws { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -87,7 +87,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES256 + dbConfiguration.encryptionAlgorithm = .AES256 do { _ = try makeDatabaseQueue(filename: "test.sqlite") XCTFail("Expected error") @@ -100,10 +100,10 @@ class GRDBSQLiteSEETests: GRDBTestCase { } } - func testDatabaseQueueWithKeyToDatabaseQueueWithNewKeyAndType() throws { + func testDatabaseQueueWithKeyToDatabaseQueueWithNewKeyAndAlgorithm() throws { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -114,7 +114,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") - try dbQueue.change(passphrase: "newSecret", encryptionType: .AES256) + try dbQueue.change(key: "newSecret", encryptionAlgorithm: .AES256) try dbQueue.inDatabase { db in try db.execute(sql: "INSERT INTO data (value) VALUES (2)") } @@ -125,7 +125,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "newSecret" - dbConfiguration.encryptionType = .AES256 + dbConfiguration.encryptionAlgorithm = .AES256 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) @@ -213,7 +213,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" let dbPool = try makeDatabasePool(filename: "test.sqlite") - try dbPool.change(passphrase: "newSecret", encryptionType: .AES128) + try dbPool.change(key: "newSecret", encryptionAlgorithm: .AES128) try dbPool.write { db in try db.execute(sql: "INSERT INTO data (value) VALUES (2)") XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) @@ -296,11 +296,11 @@ class GRDBSQLiteSEETests: GRDBTestCase { } } - func testDatabasePoolWithKeyToDatabasePoolWithWrongEncryptionType() throws { + func testDatabasePoolWithKeyToDatabasePoolWithWrongEncryptionAlgorithm() throws { let key = "secret" do { dbConfiguration.key = key - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbPool = try makeDatabasePool(filename: "test.sqlite") try dbPool.write { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -310,7 +310,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = key - dbConfiguration.encryptionType = .AES256 + dbConfiguration.encryptionAlgorithm = .AES256 do { _ = try makeDatabasePool(filename: "test.sqlite") XCTFail("Expected error") @@ -337,7 +337,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" let dbPool = try makeDatabasePool(filename: "test.sqlite") - try dbPool.change(passphrase: "newSecret", encryptionType: .AES128) + try dbPool.change(key: "newSecret", encryptionAlgorithm: .AES128) try dbPool.write { db in try db.execute(sql: "INSERT INTO data (value) VALUES (2)") XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) From e4d32b2db27d6b8871c1c21bff80b8128fe6e5b8 Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Wed, 3 Apr 2019 21:07:37 +0100 Subject: [PATCH 06/17] Rename test suites for encryption --- Tests/GRDBTests/GRDBCipherTests.swift | 2 +- Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/GRDBTests/GRDBCipherTests.swift b/Tests/GRDBTests/GRDBCipherTests.swift index 8e398666a3..e75709747e 100644 --- a/Tests/GRDBTests/GRDBCipherTests.swift +++ b/Tests/GRDBTests/GRDBCipherTests.swift @@ -2,7 +2,7 @@ import XCTest import GRDBCipher -class GRDBEncryptionTests: GRDBTestCase { +class GRDBCipherTests: GRDBTestCase { func testDatabaseQueueWithPassphraseToDatabaseQueueWithPassphrase() throws { do { diff --git a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift index 25cdd92322..08c6fe333e 100644 --- a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift +++ b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift @@ -2,7 +2,7 @@ import XCTest @testable import GRDBCustomSQLite -class GRDBSQLiteSEETests: GRDBTestCase { +class SQLiteEncryptionExtensionTests: GRDBTestCase { func testDatabaseQueueWithKeyToDatabaseQueueWithKey() throws { do { From e6d7acb896dbbce8e5aea84bbffddd64792fcafd Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Thu, 4 Apr 2019 20:56:19 +0100 Subject: [PATCH 07/17] Document including SEE in GRDBCustomSQLite builds --- Documentation/CustomSQLiteBuilds.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/CustomSQLiteBuilds.md b/Documentation/CustomSQLiteBuilds.md index 933453afa4..33ff1361eb 100644 --- a/Documentation/CustomSQLiteBuilds.md +++ b/Documentation/CustomSQLiteBuilds.md @@ -130,3 +130,30 @@ import GRDBCustomSQLite let dbQueue = try DatabaseQueue(...) ``` + + +**To install GRDB with a custom SQLite with Encryption Extension:** + +**Note:** You will need to use Swift 5 or above to use GRDB with the SQLite Encryption Extension + +1. Follow the steps above for installing a regular custom SQLite build + +2. Locate your copy of the SEE code on your system + +3. In your `GRDBCustomSQLite-USER.xcconfig`, add the following entries: + +Append `-D SQLITE_HAS_CODEC -D GRDB_SQLITE_SEE` to your `CUSTOM_OTHER_SWIFT_FLAGS` + +Add the following to the end of the file: +```SQLITE_SEE_PREFIX = /path/to/your/see-code/see-prefix.txt +SQLITE_SEE_CODE = /path/to/your/see-code/see.c +``` + +4. In your `GRDBCustomSQLite-USER.h`, add the following lines: +```#define SQLITE_HAS_CODEC +#define GRDB_SQLITE_SEE +``` + +This allows GRDB to include the needed API around the encryption extension. + +5. You can now use GRDB around the SQLite Encryption Extension by specifying a key in your database configuration From fbbfc0ea011c4f2f95097ffbdca681867509565a Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Sun, 31 Mar 2019 08:46:54 +0100 Subject: [PATCH 08/17] Update conditions for SQLCipher validation Only expose SQLCipher validation code if we are using SQLCipher and not another encryption extension. --- GRDB/Core/Configuration.swift | 2 +- GRDB/Core/Database.swift | 93 ++++++++++++++++++++++++++- GRDB/Core/DatabasePool.swift | 2 +- GRDB/Core/DatabaseQueue.swift | 2 +- GRDB/Core/DatabaseWriter.swift | 2 +- Tests/GRDBTests/EncryptionTests.swift | 2 +- 6 files changed, 97 insertions(+), 6 deletions(-) diff --git a/GRDB/Core/Configuration.swift b/GRDB/Core/Configuration.swift index 5b547e1ddc..5a0133e86d 100644 --- a/GRDB/Core/Configuration.swift +++ b/GRDB/Core/Configuration.swift @@ -76,7 +76,7 @@ public struct Configuration { // MARK: - Encryption - #if SQLITE_HAS_CODEC + #if SQLITE_HAS_CODEC && GRDBCIPHER /// The passphrase for the encrypted database. /// /// Default: nil diff --git a/GRDB/Core/Database.swift b/GRDB/Core/Database.swift index feff648620..e63a6558d6 100644 --- a/GRDB/Core/Database.swift +++ b/GRDB/Core/Database.swift @@ -202,6 +202,97 @@ extension Database { } throw DatabaseError(resultCode: .SQLITE_INTERNAL) // WTF SQLite? } + + private static func activateExtendedCodes(_ sqliteConnection: SQLiteConnection) throws { + let code = sqlite3_extended_result_codes(sqliteConnection, 1) + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + } + + #if SQLITE_HAS_CODEC && GRDBCIPHER + private static func validateSQLCipher(_ sqliteConnection: SQLiteConnection) throws { + // https://discuss.zetetic.net/t/important-advisory-sqlcipher-with-xcode-8-and-new-sdks/1688 + // + // > In order to avoid situations where SQLite might be used + // > improperly at runtime, we strongly recommend that + // > applications institute a runtime test to ensure that the + // > application is actually using SQLCipher on the active + // > connection. + var sqliteStatement: SQLiteStatement? = nil + let code = sqlite3_prepare_v2(sqliteConnection, "PRAGMA cipher_version", -1, &sqliteStatement, nil) + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + defer { + sqlite3_finalize(sqliteStatement) + } + if sqlite3_step(sqliteStatement) != SQLITE_ROW || (sqlite3_column_text(sqliteStatement, 0) == nil) { + throw DatabaseError(resultCode: .SQLITE_MISUSE, message: """ + GRDB is not linked against SQLCipher. \ + Check https://discuss.zetetic.net/t/important-advisory-sqlcipher-with-xcode-8-and-new-sdks/1688 + """) + } + } + + private static func set(passphrase: String, forConnection sqliteConnection: SQLiteConnection) throws { + let data = passphrase.data(using: .utf8)! + #if swift(>=5.0) + let code = data.withUnsafeBytes { + sqlite3_key(sqliteConnection, $0.baseAddress, Int32($0.count)) + } + #else + let code = data.withUnsafeBytes { + sqlite3_key(sqliteConnection, $0, Int32(data.count)) + } + #endif + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + } + + private static func set(cipherPageSize: Int, forConnection sqliteConnection: SQLiteConnection) throws { + var sqliteStatement: SQLiteStatement? = nil + var code = sqlite3_prepare_v2(sqliteConnection, "PRAGMA cipher_page_size = \(cipherPageSize)", -1, &sqliteStatement, nil) + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + defer { + sqlite3_finalize(sqliteStatement) + } + code = sqlite3_step(sqliteStatement) + if code != SQLITE_DONE { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + } + + private static func set(kdfIterations: Int, forConnection sqliteConnection: SQLiteConnection) throws { + var sqliteStatement: SQLiteStatement? = nil + var code = sqlite3_prepare_v2(sqliteConnection, "PRAGMA kdf_iter = \(kdfIterations)", -1, &sqliteStatement, nil) + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + defer { + sqlite3_finalize(sqliteStatement) + } + code = sqlite3_step(sqliteStatement) + if code != SQLITE_DONE { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + } + #endif + + private static func validateDatabaseFormat(_ sqliteConnection: SQLiteConnection) throws { + // Users are surprised when they open a picture as a database and + // see no error (https://github.com/groue/GRDB.swift/issues/54). + // + // So let's fail early if file is not a database, or encrypted with + // another passphrase. + let code = sqlite3_exec(sqliteConnection, "SELECT * FROM sqlite_master LIMIT 1", nil, nil, nil) + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + } } extension Database { @@ -869,7 +960,7 @@ extension Database { } } -#if SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC && GRDBCIPHER extension Database { // MARK: - Encryption diff --git a/GRDB/Core/DatabasePool.swift b/GRDB/Core/DatabasePool.swift index d2ef08cd02..e7cd1e9287 100644 --- a/GRDB/Core/DatabasePool.swift +++ b/GRDB/Core/DatabasePool.swift @@ -215,7 +215,7 @@ extension DatabasePool { #endif } -#if SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC && GRDBCIPHER extension DatabasePool { // MARK: - Encryption diff --git a/GRDB/Core/DatabaseQueue.swift b/GRDB/Core/DatabaseQueue.swift index 7d87f698af..cfad83df0d 100644 --- a/GRDB/Core/DatabaseQueue.swift +++ b/GRDB/Core/DatabaseQueue.swift @@ -132,7 +132,7 @@ extension DatabaseQueue { #endif } -#if SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC && GRDBCIPHER extension DatabaseQueue { // MARK: - Encryption diff --git a/GRDB/Core/DatabaseWriter.swift b/GRDB/Core/DatabaseWriter.swift index c313ef2ba8..b915d5ad89 100644 --- a/GRDB/Core/DatabaseWriter.swift +++ b/GRDB/Core/DatabaseWriter.swift @@ -127,7 +127,7 @@ extension DatabaseWriter { /// - precondition: database is not accessed concurrently during the /// execution of this method. public func erase() throws { - #if SQLITE_HAS_CODEC + #if SQLITE_HAS_CODEC && GRDBCIPHER // SQLCipher does not support the backup API: https://discuss.zetetic.net/t/using-the-sqlite-online-backup-api/2631 // So we'll drop all database objects one after the other. try writeWithoutTransaction { db in diff --git a/Tests/GRDBTests/EncryptionTests.swift b/Tests/GRDBTests/EncryptionTests.swift index f21f67597e..d81944c402 100644 --- a/Tests/GRDBTests/EncryptionTests.swift +++ b/Tests/GRDBTests/EncryptionTests.swift @@ -1,4 +1,4 @@ -#if SQLITE_HAS_CODEC +#if SQLITE_HAS_CODEC && GRDBCIPHER import XCTest import GRDBCipher From f68dabe42c1b2a97890bb2c90e3de00a940965fb Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Mon, 1 Apr 2019 21:29:19 +0100 Subject: [PATCH 09/17] Add support for SQLite Encryption Extension MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s all hidden behind a new flag called GRDB_SQLITE_SEE. This must be defined along with SQLITE_HAS_CODEC. --- GRDB/Core/Configuration.swift | 11 +++++++ GRDB/Core/Database.swift | 62 +++++++++++++++++++++++++++++++++++ GRDB/Core/DatabasePool.swift | 15 +++++++++ GRDB/Core/DatabaseQueue.swift | 11 +++++++ 4 files changed, 99 insertions(+) diff --git a/GRDB/Core/Configuration.swift b/GRDB/Core/Configuration.swift index 5a0133e86d..86e6b1497b 100644 --- a/GRDB/Core/Configuration.swift +++ b/GRDB/Core/Configuration.swift @@ -76,6 +76,7 @@ public struct Configuration { // MARK: - Encryption + // For SQLCipher #if SQLITE_HAS_CODEC && GRDBCIPHER /// The passphrase for the encrypted database. /// @@ -83,6 +84,16 @@ public struct Configuration { public var passphrase: String? #endif + + // For SQLite-SEE + #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + /// The key for the encrypted database. + /// + /// Default: nil + public var key: String? + + public var encryptionType: Database.EncryptionType = .AES128 + #endif /// If set, allows custom configuration to be run every time /// a new connection is opened. diff --git a/GRDB/Core/Database.swift b/GRDB/Core/Database.swift index e63a6558d6..68eacd3280 100644 --- a/GRDB/Core/Database.swift +++ b/GRDB/Core/Database.swift @@ -167,6 +167,11 @@ public final class Database { init(path: String, configuration: Configuration, schemaCache: DatabaseSchemaCache) throws { self.sqliteConnection = try Database.openConnection(path: path, flags: configuration.SQLiteOpenFlags) + #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + if let key = configuration.key { + try Database.set(key: key, withEncryptionType: configuration.encryptionType, forConnection: sqliteConnection) + } + #endif self.configuration = configuration self.schemaCache = schemaCache } @@ -282,6 +287,20 @@ extension Database { } #endif + // For SQLite-SEE + #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + private static func set(key: String, withEncryptionType encType: EncryptionType, forConnection sqliteConnection: SQLiteConnection) throws { + let prefixedKey = "\(encType.prefixValue):\(key)" + let data = prefixedKey.data(using: .utf8)! + let code = data.withUnsafeBytes { + sqlite3_key_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) + } + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + } + #endif + private static func validateDatabaseFormat(_ sqliteConnection: SQLiteConnection) throws { // Users are surprised when they open a picture as a database and // see no error (https://github.com/groue/GRDB.swift/issues/54). @@ -992,6 +1011,24 @@ extension Database { } #endif +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE +extension Database { + + // MARK: - Encryption + + func change(key: String, encryptionType:EncryptionType) throws { + let prefixedKey = "\(encryptionType.prefixValue):\(key)" + let data = prefixedKey.data(using: .utf8)! + let code = data.withUnsafeBytes { + sqlite3_rekey_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) + } + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: lastErrorMessage) + } + } +} +#endif + extension Database { /// See BusyMode and https://www.sqlite.org/c3ref/busy_handler.html @@ -1174,3 +1211,28 @@ extension Database { } } } + +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE +extension Database { + /// An SQLite Encryption Extension encryption type + /// + /// The raw value is the string to prefix a key with + public enum EncryptionType: String { + + /// - note: AES128 is the default choice for new files + case AES128 + + + case AES256 + + /// - warning: Use of RC4 for new files is not recommended + case RC4 + + + /// Prefix key strings with this value to specify type + public var prefixValue: String { + return String(describing: self).lowercased() + } + } +} +#endif diff --git a/GRDB/Core/DatabasePool.swift b/GRDB/Core/DatabasePool.swift index e7cd1e9287..dddf1903ea 100644 --- a/GRDB/Core/DatabasePool.swift +++ b/GRDB/Core/DatabasePool.swift @@ -229,6 +229,21 @@ extension DatabasePool { } } #endif +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + extension DatabasePool { + + // MARK: - Encryption + + /// Changes the passphrase of an encrypted database + public func change(passphrase: String, encryptionType: Database.EncryptionType) throws { + try readerPool.clear(andThen: { + try writer.sync { try $0.change(key: passphrase, encryptionType: encryptionType) } + readerConfig.key = passphrase + readerConfig.encryptionType = encryptionType + }) + } + } +#endif extension DatabasePool : DatabaseReader { diff --git a/GRDB/Core/DatabaseQueue.swift b/GRDB/Core/DatabaseQueue.swift index cfad83df0d..afc3339b55 100644 --- a/GRDB/Core/DatabaseQueue.swift +++ b/GRDB/Core/DatabaseQueue.swift @@ -143,6 +143,17 @@ extension DatabaseQueue { } } #endif +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE +extension DatabaseQueue { + + // MARK: - Encryption + + /// Changes the passphrase of an encrypted database + public func change(passphrase: String, encryptionType: Database.EncryptionType) throws { + try writer.sync { try $0.change(key: passphrase, encryptionType: encryptionType) } + } +} +#endif extension DatabaseQueue { From 01885a1684dd49b826c8b94cca404e48c1535ee7 Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Mon, 1 Apr 2019 21:30:30 +0100 Subject: [PATCH 10/17] Add tests to check new encryption code Also renamed existing SQLCipher tests to make distinction clear. --- GRDBCustom.xcodeproj/project.pbxproj | 18 +- ...ptionTests.swift => GRDBCipherTests.swift} | 2 +- .../SQLiteEncryptionExtensionTests.swift | 429 ++++++++++++++++++ 3 files changed, 442 insertions(+), 7 deletions(-) rename Tests/GRDBTests/{EncryptionTests.swift => GRDBCipherTests.swift} (99%) create mode 100644 Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift diff --git a/GRDBCustom.xcodeproj/project.pbxproj b/GRDBCustom.xcodeproj/project.pbxproj index d6af4d6ec3..8fb83b2dfb 100755 --- a/GRDBCustom.xcodeproj/project.pbxproj +++ b/GRDBCustom.xcodeproj/project.pbxproj @@ -42,8 +42,8 @@ 5616AAF7207CD59400AC3664 /* RequestProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5616AAF4207CD59300AC3664 /* RequestProtocols.swift */; }; 561729552235340E0006E219 /* EncodableRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 561729532235340E0006E219 /* EncodableRecord.swift */; }; 561729562235340E0006E219 /* EncodableRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = 561729532235340E0006E219 /* EncodableRecord.swift */; }; - 56176C7E1EACCD2F000F3F2B /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 56176C801EACCD31000F3F2B /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; + 56176C7E1EACCD2F000F3F2B /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* GRDBCipherTests.swift */; }; + 56176C801EACCD31000F3F2B /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* GRDBCipherTests.swift */; }; 562205FA1E420E49005860AC /* DatabasePoolReleaseMemoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563363CF1C943D13000BE133 /* DatabasePoolReleaseMemoryTests.swift */; }; 562205FB1E420E49005860AC /* DatabasePoolSchemaCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569531281C908A5B00CF1A2B /* DatabasePoolSchemaCacheTests.swift */; }; 562205FC1E420E49005860AC /* DatabaseQueueReleaseMemoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563363D41C94484E000BE133 /* DatabaseQueueReleaseMemoryTests.swift */; }; @@ -428,6 +428,8 @@ 56FF455D1D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */; }; 6340BF831E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; 6340BF871E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; + B5C0503F2252574500CA794A /* SQLiteEncryptionExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C0503E2252574500CA794A /* SQLiteEncryptionExtensionTests.swift */; }; + B5C050402252574500CA794A /* SQLiteEncryptionExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C0503E2252574500CA794A /* SQLiteEncryptionExtensionTests.swift */; }; EED476F21CFD17270026A4EC /* GRDBCustomSQLite-USER.h in Headers */ = {isa = PBXBuildFile; fileRef = EED476F11CFD16FF0026A4EC /* GRDBCustomSQLite-USER.h */; settings = {ATTRIBUTES = (Public, ); }; }; EED476F31CFD172C0026A4EC /* GRDBCustomSQLite-USER.h in Headers */ = {isa = PBXBuildFile; fileRef = EED476F11CFD16FF0026A4EC /* GRDBCustomSQLite-USER.h */; settings = {ATTRIBUTES = (Public, ); }; }; F3BA800A1CFB286A003DC1BA /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56A238701B9C75030082EB20 /* Configuration.swift */; }; @@ -824,7 +826,7 @@ 56703299212B5461007D270F /* DatabaseUUIDEncodingStrategyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseUUIDEncodingStrategyTests.swift; sourceTree = ""; }; 567071F2208A00BE006AD95A /* SQLiteDateParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteDateParser.swift; sourceTree = ""; }; 567156151CB142AA007DC145 /* DatabaseQueueReadOnlyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueReadOnlyTests.swift; sourceTree = ""; }; - 567156701CB18050007DC145 /* EncryptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionTests.swift; sourceTree = ""; }; + 567156701CB18050007DC145 /* GRDBCipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GRDBCipherTests.swift; sourceTree = ""; }; 5671FC1F1DA3CAC9003BF4FF /* FTS3TokenizerDescriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS3TokenizerDescriptor.swift; sourceTree = ""; }; 5672DE581CDB72520022BA81 /* DatabaseQueueBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueBackupTests.swift; sourceTree = ""; }; 5672DE661CDB751D0022BA81 /* DatabasePoolBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabasePoolBackupTests.swift; sourceTree = ""; }; @@ -979,6 +981,7 @@ 56FF453F1D2C23BA00F21EF9 /* MutablePersistableRecordDeleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutablePersistableRecordDeleteTests.swift; sourceTree = ""; }; 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordUniqueIndexTests.swift; sourceTree = ""; }; 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordPersistenceConflictPolicy.swift; sourceTree = ""; }; + B5C0503E2252574500CA794A /* SQLiteEncryptionExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteEncryptionExtensionTests.swift; sourceTree = ""; }; DC2393C61ABE35F8003FF113 /* GRDB-Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GRDB-Bridging.h"; sourceTree = ""; }; DC3773F719C8CBB3004FCF85 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DC3773F819C8CBB3004FCF85 /* GRDB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GRDB.h; sourceTree = ""; }; @@ -1211,7 +1214,8 @@ 56176CA01EACEE2A000F3F2B /* GRDBCipher */ = { isa = PBXGroup; children = ( - 567156701CB18050007DC145 /* EncryptionTests.swift */, + 567156701CB18050007DC145 /* GRDBCipherTests.swift */, + B5C0503E2252574500CA794A /* SQLiteEncryptionExtensionTests.swift */, ); name = GRDBCipher; sourceTree = ""; @@ -2150,6 +2154,7 @@ 5653EB7D20961FB200F46237 /* AssociationBelongsToSQLTests.swift in Sources */, 563B070021861D9D00B38F35 /* ValueObservationCountTests.swift in Sources */, 5698AC0A1D9B9FCF0056AF8C /* QueryInterfaceExtensibilityTests.swift in Sources */, + B5C050402252574500CA794A /* SQLiteEncryptionExtensionTests.swift in Sources */, 56EA63CA209C7F1E009715B8 /* DerivableRequestTests.swift in Sources */, F3BA80E91CFB3016003DC1BA /* RowAdapterTests.swift in Sources */, 562393371DEDFC5700A6B01F /* AnyCursorTests.swift in Sources */, @@ -2196,7 +2201,7 @@ 563B071321862C3E00B38F35 /* ValueObservationRecordTests.swift in Sources */, 56B6EF61208CB746002F0ACB /* ColumnExpressionTests.swift in Sources */, F3BA81121CFB3059003DC1BA /* DatabaseMigratorTests.swift in Sources */, - 56176C801EACCD31000F3F2B /* EncryptionTests.swift in Sources */, + 56176C801EACCD31000F3F2B /* GRDBCipherTests.swift in Sources */, F3BA80EB1CFB3016003DC1BA /* FetchableRecordTests.swift in Sources */, 56B86E71220FF4C900524C16 /* SQLLiteralTests.swift in Sources */, F3BA81151CFB305E003DC1BA /* Record+QueryInterfaceRequestTests.swift in Sources */, @@ -2467,6 +2472,7 @@ 5653EB7C20961FB200F46237 /* AssociationBelongsToSQLTests.swift in Sources */, 563B06FF21861D9D00B38F35 /* ValueObservationCountTests.swift in Sources */, 5698ACDA1DA925430056AF8C /* RowTestCase.swift in Sources */, + B5C0503F2252574500CA794A /* SQLiteEncryptionExtensionTests.swift in Sources */, 56EA63C9209C7F1E009715B8 /* DerivableRequestTests.swift in Sources */, F3BA811D1CFB305F003DC1BA /* TableRecord+QueryInterfaceRequestTests.swift in Sources */, F3BA80AD1CFB2FA6003DC1BA /* DataMemoryTests.swift in Sources */, @@ -2514,7 +2520,7 @@ 56B6EF60208CB746002F0ACB /* ColumnExpressionTests.swift in Sources */, 562205FD1E420EA2005860AC /* DatabasePoolBackupTests.swift in Sources */, 562206091E420EB2005860AC /* DatabaseQueueConcurrencyTests.swift in Sources */, - 56176C7E1EACCD2F000F3F2B /* EncryptionTests.swift in Sources */, + 56176C7E1EACCD2F000F3F2B /* GRDBCipherTests.swift in Sources */, 56B86E70220FF4C900524C16 /* SQLLiteralTests.swift in Sources */, F3BA811C1CFB305F003DC1BA /* QueryInterfaceExpressionsTests.swift in Sources */, 5698ACD11DA8C2620056AF8C /* RecordPrimaryKeyHiddenRowIDTests.swift in Sources */, diff --git a/Tests/GRDBTests/EncryptionTests.swift b/Tests/GRDBTests/GRDBCipherTests.swift similarity index 99% rename from Tests/GRDBTests/EncryptionTests.swift rename to Tests/GRDBTests/GRDBCipherTests.swift index d81944c402..503d39b5e2 100644 --- a/Tests/GRDBTests/EncryptionTests.swift +++ b/Tests/GRDBTests/GRDBCipherTests.swift @@ -2,7 +2,7 @@ import XCTest import GRDBCipher -class EncryptionTests: GRDBTestCase { +class GRDBEncryptionTests: GRDBTestCase { func testDatabaseQueueWithPassphraseToDatabaseQueueWithPassphrase() throws { do { diff --git a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift new file mode 100644 index 0000000000..47c1103804 --- /dev/null +++ b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift @@ -0,0 +1,429 @@ +#if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE +import XCTest +@testable import GRDBCustomSQLite + +class GRDBSQLiteSEETests: GRDBTestCase { + + func testDatabaseQueueWithKeyToDatabaseQueueWithKey() throws { + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithoutKey() throws { + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = nil + do { + _ = try makeDatabaseQueue(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithWrongKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "wrong" + do { + _ = try makeDatabaseQueue(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithWrongEncryptionType() throws { + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES256 + do { + _ = try makeDatabaseQueue(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithNewKeyAndType() throws { + do { + dbConfiguration.key = "secret" + dbConfiguration.encryptionType = .AES128 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.change(passphrase: "newSecret", encryptionType: .AES256) + try dbQueue.inDatabase { db in + try db.execute(sql: "INSERT INTO data (value) VALUES (2)") + } + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + + do { + dbConfiguration.key = "newSecret" + dbConfiguration.encryptionType = .AES256 + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + } + + func testDatabaseQueueWithKeyToDatabasePoolWithKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.read { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + + func testDatabaseQueueWithKeyToDatabasePoolWithoutKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = nil + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabasePoolWithWrongKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "wrong" + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabasePoolWithNewKey() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.change(passphrase: "newSecret", encryptionType: .AES128) + try dbPool.write { db in + try db.execute(sql: "INSERT INTO data (value) VALUES (2)") + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + + do { + dbConfiguration.key = "newSecret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.read { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithKey() throws { + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.read { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithoutKey() throws { + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = nil + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithWrongKey() throws { + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "wrong" + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithWrongEncryptionType() throws { + let key = "secret" + do { + dbConfiguration.key = key + dbConfiguration.encryptionType = .AES128 + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = key + dbConfiguration.encryptionType = .AES256 + do { + _ = try makeDatabasePool(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabasePoolWithKeyToDatabasePoolWithNewKey() throws { + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.write { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.change(passphrase: "newSecret", encryptionType: .AES128) + try dbPool.write { db in + try db.execute(sql: "INSERT INTO data (value) VALUES (2)") + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + + do { + dbConfiguration.key = "newSecret" + let dbPool = try makeDatabasePool(filename: "test.sqlite") + try dbPool.read { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) + } + } + } + + func testDatabaseQueueWithPragmaKeyToDatabaseQueueWithKey() throws { + do { + dbConfiguration.key = nil + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "PRAGMA key = 'secret'") + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + + func testDatabaseQueueWithPragmaKeyToDatabaseQueueWithoutKey() throws { + do { + dbConfiguration.key = nil + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "PRAGMA key = 'secret'") + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = nil + do { + _ = try makeDatabaseQueue(filename: "test.sqlite") + XCTFail("Expected error") + } catch let error as DatabaseError { + XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) + XCTAssertEqual(error.message!, "file is not a database") + XCTAssertTrue(error.sql == nil) + XCTAssertEqual(error.description, "SQLite error 26: file is not a database") + } + } + } + + func testDatabaseQueueWithKeyToDatabaseQueueWithPragmaRemovedKeyAndType() throws { + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "CREATE TABLE data (value INTEGER)") + try db.execute(sql: "INSERT INTO data (value) VALUES (1)") + } + } + + do { + dbConfiguration.key = "secret" + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + try db.execute(sql: "PRAGMA rekey = ''") + } + } + + do { + dbConfiguration.key = nil + let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") + try dbQueue.inDatabase { db in + XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) + } + } + } + +} +#endif From 6eaf5c87b5912d9209af108f3e505be2975317b7 Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Wed, 3 Apr 2019 13:58:41 +0100 Subject: [PATCH 11/17] Add renamed tests to other targets Missing from last commit --- GRDB.xcodeproj/project.pbxproj | 13 +++++++------ GRDBCipher.xcodeproj/project.pbxproj | 17 +++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/GRDB.xcodeproj/project.pbxproj b/GRDB.xcodeproj/project.pbxproj index fa5ed150e2..7fdd6495ff 100755 --- a/GRDB.xcodeproj/project.pbxproj +++ b/GRDB.xcodeproj/project.pbxproj @@ -97,8 +97,6 @@ 56176C6E1EACCCC9000F3F2B /* FTS5TableBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56B964C21DA521450002DA19 /* FTS5TableBuilderTests.swift */; }; 56176C6F1EACCCC9000F3F2B /* FTS5TokenizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5698ACCA1DA62A2D0056AF8C /* FTS5TokenizerTests.swift */; }; 56176C701EACCCC9000F3F2B /* FTS5WrapperTokenizerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56ED8A7E1DAB8D6800BD0ABC /* FTS5WrapperTokenizerTests.swift */; }; - 56176C7D1EACCD2D000F3F2B /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 56176C7F1EACCD2F000F3F2B /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; 56193E8E1CD8A3E200F95862 /* FetchedRecordsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A7787C1C6A4DD600F507F6 /* FetchedRecordsController.swift */; }; 562205F11E420E47005860AC /* DatabasePoolReleaseMemoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563363CF1C943D13000BE133 /* DatabasePoolReleaseMemoryTests.swift */; }; 562205F21E420E47005860AC /* DatabasePoolSchemaCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 569531281C908A5B00CF1A2B /* DatabasePoolSchemaCacheTests.swift */; }; @@ -797,6 +795,8 @@ 56FF455A1D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */; }; 6340BF801E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; 6340BF841E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; + B501B3482254A5440071DCDC /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501B3472254A5440071DCDC /* GRDBCipherTests.swift */; }; + B501B3492254A5440071DCDC /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501B3472254A5440071DCDC /* GRDBCipherTests.swift */; }; C96C0F2B2084A442006B2981 /* SQLiteDateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C96C0F242084A442006B2981 /* SQLiteDateParser.swift */; }; C96C0F2C2084A459006B2981 /* SQLiteDateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C96C0F242084A442006B2981 /* SQLiteDateParser.swift */; }; C96C0F2D2084A45A006B2981 /* SQLiteDateParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C96C0F242084A442006B2981 /* SQLiteDateParser.swift */; }; @@ -1072,7 +1072,6 @@ 56703290212B544F007D270F /* DatabaseUUIDEncodingStrategyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseUUIDEncodingStrategyTests.swift; sourceTree = ""; }; 567071FA208A509C006AD95A /* DateParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateParsingTests.swift; sourceTree = ""; }; 567156151CB142AA007DC145 /* DatabaseQueueReadOnlyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueReadOnlyTests.swift; sourceTree = ""; }; - 567156701CB18050007DC145 /* EncryptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionTests.swift; sourceTree = ""; }; 5671FC1F1DA3CAC9003BF4FF /* FTS3TokenizerDescriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS3TokenizerDescriptor.swift; sourceTree = ""; }; 5672DE581CDB72520022BA81 /* DatabaseQueueBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueBackupTests.swift; sourceTree = ""; }; 5672DE661CDB751D0022BA81 /* DatabasePoolBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabasePoolBackupTests.swift; sourceTree = ""; }; @@ -1268,6 +1267,7 @@ 56FF453F1D2C23BA00F21EF9 /* MutablePersistableRecordDeleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutablePersistableRecordDeleteTests.swift; sourceTree = ""; }; 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordUniqueIndexTests.swift; sourceTree = ""; }; 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordPersistenceConflictPolicy.swift; sourceTree = ""; }; + B501B3472254A5440071DCDC /* GRDBCipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GRDBCipherTests.swift; sourceTree = ""; }; C96C0F242084A442006B2981 /* SQLiteDateParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteDateParser.swift; sourceTree = ""; }; DC2393C61ABE35F8003FF113 /* GRDB-Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GRDB-Bridging.h"; sourceTree = ""; }; DC3773F319C8CBB3004FCF85 /* GRDB.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GRDB.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1507,7 +1507,7 @@ 56176CA01EACEE2A000F3F2B /* GRDBCipher */ = { isa = PBXGroup; children = ( - 567156701CB18050007DC145 /* EncryptionTests.swift */, + B501B3472254A5440071DCDC /* GRDBCipherTests.swift */, ); name = GRDBCipher; sourceTree = ""; @@ -2318,6 +2318,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -2843,6 +2844,7 @@ 5657AB421D108BA9006283EF /* FoundationNSDataTests.swift in Sources */, 56CC922D201DFFB900CB597E /* DropWhileCursorTests.swift in Sources */, 56D507621F6BAE8600AE1C5B /* PrimaryKeyInfoTests.swift in Sources */, + B501B3492254A5440071DCDC /* GRDBCipherTests.swift in Sources */, 5653EADD20944B4F00F46237 /* AssociationBelongsToSQLTests.swift in Sources */, 56176C6B1EACCCC9000F3F2B /* FTS5CustomTokenizerTests.swift in Sources */, 56300B621C53C42C005A543B /* FetchableRecord+QueryInterfaceRequestTests.swift in Sources */, @@ -2859,7 +2861,6 @@ 56EB0AB31BCD787300A3DC55 /* DataMemoryTests.swift in Sources */, 56B6EF57208CB4E3002F0ACB /* ColumnExpressionTests.swift in Sources */, 563B071621862C4700B38F35 /* ValueObservationRecordTests.swift in Sources */, - 56176C7F1EACCD2F000F3F2B /* EncryptionTests.swift in Sources */, 56741EAC1E66A8B3003E422D /* FetchRequestTests.swift in Sources */, 5672DE5C1CDB72520022BA81 /* DatabaseQueueBackupTests.swift in Sources */, 56A2385E1B9C74A90082EB20 /* RecordCopyTests.swift in Sources */, @@ -3032,6 +3033,7 @@ 56CC9251201E093F00CB597E /* PrefixCursorTests.swift in Sources */, 56D4965E1D81304E008276D7 /* FoundationNSStringTests.swift in Sources */, 5698AC891DA389380056AF8C /* FTS3TableBuilderTests.swift in Sources */, + B501B3482254A5440071DCDC /* GRDBCipherTests.swift in Sources */, 56CC922C201DFFB900CB597E /* DropWhileCursorTests.swift in Sources */, 5653EADC20944B4F00F46237 /* AssociationBelongsToSQLTests.swift in Sources */, 56D5075E1F6BAE8600AE1C5B /* PrimaryKeyInfoTests.swift in Sources */, @@ -3050,7 +3052,6 @@ 563B071521862C4700B38F35 /* ValueObservationRecordTests.swift in Sources */, 56B6EF56208CB4E3002F0ACB /* ColumnExpressionTests.swift in Sources */, 5698ACD71DA925420056AF8C /* RowTestCase.swift in Sources */, - 56176C7D1EACCD2D000F3F2B /* EncryptionTests.swift in Sources */, 56741EA81E66A8B3003E422D /* FetchRequestTests.swift in Sources */, 56B86E79220FF4E000524C16 /* SQLLiteralTests.swift in Sources */, 56D496831D813147008276D7 /* DatabaseSavepointTests.swift in Sources */, diff --git a/GRDBCipher.xcodeproj/project.pbxproj b/GRDBCipher.xcodeproj/project.pbxproj index cdc5ba54ac..6575b8dad0 100755 --- a/GRDBCipher.xcodeproj/project.pbxproj +++ b/GRDBCipher.xcodeproj/project.pbxproj @@ -591,10 +591,6 @@ 567DAF371EAB789800FC0928 /* DatabaseLogErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567DAF341EAB789800FC0928 /* DatabaseLogErrorTests.swift */; }; 567DAF3A1EAB789800FC0928 /* DatabaseLogErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567DAF341EAB789800FC0928 /* DatabaseLogErrorTests.swift */; }; 567DAF3B1EAB789800FC0928 /* DatabaseLogErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567DAF341EAB789800FC0928 /* DatabaseLogErrorTests.swift */; }; - 567E55ED1D2BDD3D00CC6F79 /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 567E55EE1D2BDD3F00CC6F79 /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 567E55F31D2BDDFE00CC6F79 /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; - 567E55F41D2BDDFF00CC6F79 /* EncryptionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567156701CB18050007DC145 /* EncryptionTests.swift */; }; 567F0B33220F196F00D111FB /* SQLInterpolationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567F0B32220F196F00D111FB /* SQLInterpolationTests.swift */; }; 567F0B34220F196F00D111FB /* SQLInterpolationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567F0B32220F196F00D111FB /* SQLInterpolationTests.swift */; }; 567F0B35220F196F00D111FB /* SQLInterpolationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 567F0B32220F196F00D111FB /* SQLInterpolationTests.swift */; }; @@ -995,6 +991,8 @@ 6340BF821E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; 6340BF851E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; 6340BF861E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */; }; + B501B34C2254A56B0071DCDC /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501B34B2254A56B0071DCDC /* GRDBCipherTests.swift */; }; + B501B34D2254A56B0071DCDC /* GRDBCipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B501B34B2254A56B0071DCDC /* GRDBCipherTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1208,7 +1206,6 @@ 567071F6208A00D4006AD95A /* SQLiteDateParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteDateParser.swift; sourceTree = ""; }; 567156151CB142AA007DC145 /* DatabaseQueueReadOnlyTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueReadOnlyTests.swift; sourceTree = ""; }; 5671566C1CB16729007DC145 /* GRDBCipherOSXEncryptedTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GRDBCipherOSXEncryptedTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 567156701CB18050007DC145 /* EncryptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionTests.swift; sourceTree = ""; }; 5671FC1F1DA3CAC9003BF4FF /* FTS3TokenizerDescriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FTS3TokenizerDescriptor.swift; sourceTree = ""; }; 5672DE581CDB72520022BA81 /* DatabaseQueueBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseQueueBackupTests.swift; sourceTree = ""; }; 5672DE661CDB751D0022BA81 /* DatabasePoolBackupTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabasePoolBackupTests.swift; sourceTree = ""; }; @@ -1371,6 +1368,7 @@ 56FF453F1D2C23BA00F21EF9 /* MutablePersistableRecordDeleteTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutablePersistableRecordDeleteTests.swift; sourceTree = ""; }; 56FF45551D2CDA5200F21EF9 /* RecordUniqueIndexTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordUniqueIndexTests.swift; sourceTree = ""; }; 6340BF7F1E5E3F7900832805 /* RecordPersistenceConflictPolicy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RecordPersistenceConflictPolicy.swift; sourceTree = ""; }; + B501B34B2254A56B0071DCDC /* GRDBCipherTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GRDBCipherTests.swift; sourceTree = ""; }; DC2393C61ABE35F8003FF113 /* GRDB-Bridging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GRDB-Bridging.h"; sourceTree = ""; }; DC3773F719C8CBB3004FCF85 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DC3773F819C8CBB3004FCF85 /* GRDB.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GRDB.h; sourceTree = ""; }; @@ -1621,7 +1619,7 @@ 56176CA01EACEE2A000F3F2B /* GRDBCipher */ = { isa = PBXGroup; children = ( - 567156701CB18050007DC145 /* EncryptionTests.swift */, + B501B34B2254A56B0071DCDC /* GRDBCipherTests.swift */, ); name = GRDBCipher; sourceTree = ""; @@ -2285,6 +2283,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -2559,6 +2558,7 @@ 560FC5791CB00B880014AA8E /* RecordPrimaryKeyMultipleTests.swift in Sources */, 56CC9257201E094F00CB597E /* PrefixCursorTests.swift in Sources */, 5698AC971DA4B0430056AF8C /* FTS4RecordTests.swift in Sources */, + B501B34C2254A56B0071DCDC /* GRDBCipherTests.swift in Sources */, 5657AB4F1D108BA9006283EF /* FoundationNSNumberTests.swift in Sources */, 56CC9236201E009700CB597E /* DropWhileCursorTests.swift in Sources */, 56D5075F1F6BAE8600AE1C5B /* PrimaryKeyInfoTests.swift in Sources */, @@ -2593,7 +2593,6 @@ 5665F86C203EF47D0084C6C0 /* ColumnInfoTests.swift in Sources */, 560FC5811CB00B880014AA8E /* RecordCopyTests.swift in Sources */, 560FC5821CB00B880014AA8E /* RawRepresentable+DatabaseValueConvertibleTests.swift in Sources */, - 567E55ED1D2BDD3D00CC6F79 /* EncryptionTests.swift in Sources */, 56741EA91E66A8B3003E422D /* FetchRequestTests.swift in Sources */, 56176C641EACCCC7000F3F2B /* FTS5WrapperTokenizerTests.swift in Sources */, 5653EBB820961FE800F46237 /* AssociationParallelSQLTests.swift in Sources */, @@ -2767,7 +2766,6 @@ 56B6EF65208CB762002F0ACB /* ColumnExpressionTests.swift in Sources */, 5671563C1CB16729007DC145 /* MutablePersistableRecordTests.swift in Sources */, 5671563D1CB16729007DC145 /* FetchableRecord+QueryInterfaceRequestTests.swift in Sources */, - 567E55F31D2BDDFE00CC6F79 /* EncryptionTests.swift in Sources */, 56B86E7E220FF4F500524C16 /* SQLLiteralTests.swift in Sources */, 5671563F1CB16729007DC145 /* RecordCopyTests.swift in Sources */, 5698ACB81DA6285E0056AF8C /* FTS3TokenizerTests.swift in Sources */, @@ -3024,7 +3022,6 @@ 562EA82B1F17B2AC00FA528C /* CompilationProtocolTests.swift in Sources */, 56DAA2D71DE99DAB006E10C8 /* DatabaseCursorTests.swift in Sources */, 5698AC4E1DA2D48A0056AF8C /* FTS3RecordTests.swift in Sources */, - 567E55EE1D2BDD3F00CC6F79 /* EncryptionTests.swift in Sources */, 56AFCA361CB1AA9900F48B96 /* DatabaseCollationTests.swift in Sources */, 56AFCA371CB1AA9900F48B96 /* StatementColumnConvertibleFetchTests.swift in Sources */, 56AFCA381CB1AA9900F48B96 /* QueryInterfaceExpressionsTests.swift in Sources */, @@ -3065,6 +3062,7 @@ 566AD8CB1D531BED002EC1A8 /* TableDefinitionTests.swift in Sources */, 56AFCA4B1CB1AA9900F48B96 /* MutablePersistableRecordTests.swift in Sources */, 56CC9259201E094F00CB597E /* PrefixCursorTests.swift in Sources */, + B501B34D2254A56B0071DCDC /* GRDBCipherTests.swift in Sources */, 5657AB431D108BA9006283EF /* FoundationNSDataTests.swift in Sources */, 56AFCA4C1CB1AA9900F48B96 /* FetchableRecord+QueryInterfaceRequestTests.swift in Sources */, 56CC9238201E009900CB597E /* DropWhileCursorTests.swift in Sources */, @@ -3214,7 +3212,6 @@ 56DAA2D81DE99DAB006E10C8 /* DatabaseCursorTests.swift in Sources */, 5698AC4F1DA2D48A0056AF8C /* FTS3RecordTests.swift in Sources */, 56AFCA8F1CB1ABC800F48B96 /* DatabaseCollationTests.swift in Sources */, - 567E55F41D2BDDFF00CC6F79 /* EncryptionTests.swift in Sources */, 56AFCA901CB1ABC800F48B96 /* StatementColumnConvertibleFetchTests.swift in Sources */, 5690C33D1D23E7D200E59934 /* FoundationDateTests.swift in Sources */, 5672DE6C1CDB751D0022BA81 /* DatabasePoolBackupTests.swift in Sources */, From 8f331120b70df431afcf3c966b276cbce354d7b2 Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Wed, 3 Apr 2019 20:58:08 +0100 Subject: [PATCH 12/17] Renaming things for consistency EncryptionType -> Algorithm passphrase -> key Tidy enum values --- GRDB/Core/Configuration.swift | 10 +++++-- GRDB/Core/Database.swift | 22 +++++--------- GRDB/Core/DatabasePool.swift | 10 +++---- GRDB/Core/DatabaseQueue.swift | 6 ++-- .../SQLiteEncryptionExtensionTests.swift | 30 +++++++++---------- 5 files changed, 39 insertions(+), 39 deletions(-) diff --git a/GRDB/Core/Configuration.swift b/GRDB/Core/Configuration.swift index 86e6b1497b..c552d77081 100644 --- a/GRDB/Core/Configuration.swift +++ b/GRDB/Core/Configuration.swift @@ -87,12 +87,18 @@ public struct Configuration { // For SQLite-SEE #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE - /// The key for the encrypted database. + + /// The key for an attempting to open an encrypted database. + /// + /// This is ignored if set to nil /// /// Default: nil public var key: String? - public var encryptionType: Database.EncryptionType = .AES128 + /// Which algorithm to use when opening an encrypted database + /// + /// This is only used if a key is set to non-nil + public var encryptionAlgorithm: Database.EncryptionAlgorithm = .AES128 #endif /// If set, allows custom configuration to be run every time diff --git a/GRDB/Core/Database.swift b/GRDB/Core/Database.swift index 68eacd3280..2bec17fde5 100644 --- a/GRDB/Core/Database.swift +++ b/GRDB/Core/Database.swift @@ -289,8 +289,8 @@ extension Database { // For SQLite-SEE #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE - private static func set(key: String, withEncryptionType encType: EncryptionType, forConnection sqliteConnection: SQLiteConnection) throws { - let prefixedKey = "\(encType.prefixValue):\(key)" + private static func set(key: String, withEncryptionAlgorithm algorithm: EncryptionAlgorithm, forConnection sqliteConnection: SQLiteConnection) throws { + let prefixedKey = "\(algorithm.rawValue):\(key)" let data = prefixedKey.data(using: .utf8)! let code = data.withUnsafeBytes { sqlite3_key_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) @@ -1016,8 +1016,8 @@ extension Database { // MARK: - Encryption - func change(key: String, encryptionType:EncryptionType) throws { - let prefixedKey = "\(encryptionType.prefixValue):\(key)" + func change(key: String, encryptionAlgorithm:EncryptionAlgorithm) throws { + let prefixedKey = "\(encryptionAlgorithm.rawValue):\(key)" let data = prefixedKey.data(using: .utf8)! let code = data.withUnsafeBytes { sqlite3_rekey_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) @@ -1217,22 +1217,16 @@ extension Database { /// An SQLite Encryption Extension encryption type /// /// The raw value is the string to prefix a key with - public enum EncryptionType: String { + public enum EncryptionAlgorithm: String { /// - note: AES128 is the default choice for new files - case AES128 + case AES128 = "aes128" - case AES256 + case AES256 = "aes256" /// - warning: Use of RC4 for new files is not recommended - case RC4 - - - /// Prefix key strings with this value to specify type - public var prefixValue: String { - return String(describing: self).lowercased() - } + case RC4 = "rc4" } } #endif diff --git a/GRDB/Core/DatabasePool.swift b/GRDB/Core/DatabasePool.swift index dddf1903ea..dd74ba35a3 100644 --- a/GRDB/Core/DatabasePool.swift +++ b/GRDB/Core/DatabasePool.swift @@ -234,12 +234,12 @@ extension DatabasePool { // MARK: - Encryption - /// Changes the passphrase of an encrypted database - public func change(passphrase: String, encryptionType: Database.EncryptionType) throws { + /// Changes the key of an encrypted database + public func change(key: String, encryptionAlgorithm: Database.EncryptionAlgorithm) throws { try readerPool.clear(andThen: { - try writer.sync { try $0.change(key: passphrase, encryptionType: encryptionType) } - readerConfig.key = passphrase - readerConfig.encryptionType = encryptionType + try writer.sync { try $0.change(key: key, encryptionAlgorithm: encryptionAlgorithm) } + readerConfig.key = key + readerConfig.encryptionAlgorithm = encryptionAlgorithm }) } } diff --git a/GRDB/Core/DatabaseQueue.swift b/GRDB/Core/DatabaseQueue.swift index afc3339b55..e3c8641223 100644 --- a/GRDB/Core/DatabaseQueue.swift +++ b/GRDB/Core/DatabaseQueue.swift @@ -148,9 +148,9 @@ extension DatabaseQueue { // MARK: - Encryption - /// Changes the passphrase of an encrypted database - public func change(passphrase: String, encryptionType: Database.EncryptionType) throws { - try writer.sync { try $0.change(key: passphrase, encryptionType: encryptionType) } + /// Changes the key of an encrypted database + public func change(key: String, encryptionAlgorithm: Database.EncryptionAlgorithm) throws { + try writer.sync { try $0.change(key: key, encryptionAlgorithm: encryptionAlgorithm) } } } #endif diff --git a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift index 47c1103804..25cdd92322 100644 --- a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift +++ b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift @@ -7,7 +7,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { func testDatabaseQueueWithKeyToDatabaseQueueWithKey() throws { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -17,7 +17,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 1) @@ -28,7 +28,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { func testDatabaseQueueWithKeyToDatabaseQueueWithoutKey() throws { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -74,10 +74,10 @@ class GRDBSQLiteSEETests: GRDBTestCase { } } - func testDatabaseQueueWithKeyToDatabaseQueueWithWrongEncryptionType() throws { + func testDatabaseQueueWithKeyToDatabaseQueueWithWrongEncryptionAlgorithm() throws { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -87,7 +87,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES256 + dbConfiguration.encryptionAlgorithm = .AES256 do { _ = try makeDatabaseQueue(filename: "test.sqlite") XCTFail("Expected error") @@ -100,10 +100,10 @@ class GRDBSQLiteSEETests: GRDBTestCase { } } - func testDatabaseQueueWithKeyToDatabaseQueueWithNewKeyAndType() throws { + func testDatabaseQueueWithKeyToDatabaseQueueWithNewKeyAndAlgorithm() throws { do { dbConfiguration.key = "secret" - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -114,7 +114,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") - try dbQueue.change(passphrase: "newSecret", encryptionType: .AES256) + try dbQueue.change(key: "newSecret", encryptionAlgorithm: .AES256) try dbQueue.inDatabase { db in try db.execute(sql: "INSERT INTO data (value) VALUES (2)") } @@ -125,7 +125,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "newSecret" - dbConfiguration.encryptionType = .AES256 + dbConfiguration.encryptionAlgorithm = .AES256 let dbQueue = try makeDatabaseQueue(filename: "test.sqlite") try dbQueue.inDatabase { db in XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) @@ -213,7 +213,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" let dbPool = try makeDatabasePool(filename: "test.sqlite") - try dbPool.change(passphrase: "newSecret", encryptionType: .AES128) + try dbPool.change(key: "newSecret", encryptionAlgorithm: .AES128) try dbPool.write { db in try db.execute(sql: "INSERT INTO data (value) VALUES (2)") XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) @@ -296,11 +296,11 @@ class GRDBSQLiteSEETests: GRDBTestCase { } } - func testDatabasePoolWithKeyToDatabasePoolWithWrongEncryptionType() throws { + func testDatabasePoolWithKeyToDatabasePoolWithWrongEncryptionAlgorithm() throws { let key = "secret" do { dbConfiguration.key = key - dbConfiguration.encryptionType = .AES128 + dbConfiguration.encryptionAlgorithm = .AES128 let dbPool = try makeDatabasePool(filename: "test.sqlite") try dbPool.write { db in try db.execute(sql: "CREATE TABLE data (value INTEGER)") @@ -310,7 +310,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = key - dbConfiguration.encryptionType = .AES256 + dbConfiguration.encryptionAlgorithm = .AES256 do { _ = try makeDatabasePool(filename: "test.sqlite") XCTFail("Expected error") @@ -337,7 +337,7 @@ class GRDBSQLiteSEETests: GRDBTestCase { do { dbConfiguration.key = "secret" let dbPool = try makeDatabasePool(filename: "test.sqlite") - try dbPool.change(passphrase: "newSecret", encryptionType: .AES128) + try dbPool.change(key: "newSecret", encryptionAlgorithm: .AES128) try dbPool.write { db in try db.execute(sql: "INSERT INTO data (value) VALUES (2)") XCTAssertEqual(try Int.fetchOne(db, sql: "SELECT COUNT(*) FROM data")!, 2) From 59f5316af8d6d9f0cb9bd6c1c291fda9d2f24fb7 Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Wed, 3 Apr 2019 21:07:37 +0100 Subject: [PATCH 13/17] Rename test suites for encryption --- Tests/GRDBTests/GRDBCipherTests.swift | 2 +- Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/GRDBTests/GRDBCipherTests.swift b/Tests/GRDBTests/GRDBCipherTests.swift index 503d39b5e2..592d1bf7a6 100644 --- a/Tests/GRDBTests/GRDBCipherTests.swift +++ b/Tests/GRDBTests/GRDBCipherTests.swift @@ -2,7 +2,7 @@ import XCTest import GRDBCipher -class GRDBEncryptionTests: GRDBTestCase { +class GRDBCipherTests: GRDBTestCase { func testDatabaseQueueWithPassphraseToDatabaseQueueWithPassphrase() throws { do { diff --git a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift index 25cdd92322..08c6fe333e 100644 --- a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift +++ b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift @@ -2,7 +2,7 @@ import XCTest @testable import GRDBCustomSQLite -class GRDBSQLiteSEETests: GRDBTestCase { +class SQLiteEncryptionExtensionTests: GRDBTestCase { func testDatabaseQueueWithKeyToDatabaseQueueWithKey() throws { do { From 63f9ccc85498046678fd86b8af0207ab9b7bc5b1 Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Thu, 4 Apr 2019 20:56:19 +0100 Subject: [PATCH 14/17] Document including SEE in GRDBCustomSQLite builds --- Documentation/CustomSQLiteBuilds.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Documentation/CustomSQLiteBuilds.md b/Documentation/CustomSQLiteBuilds.md index 933453afa4..33ff1361eb 100644 --- a/Documentation/CustomSQLiteBuilds.md +++ b/Documentation/CustomSQLiteBuilds.md @@ -130,3 +130,30 @@ import GRDBCustomSQLite let dbQueue = try DatabaseQueue(...) ``` + + +**To install GRDB with a custom SQLite with Encryption Extension:** + +**Note:** You will need to use Swift 5 or above to use GRDB with the SQLite Encryption Extension + +1. Follow the steps above for installing a regular custom SQLite build + +2. Locate your copy of the SEE code on your system + +3. In your `GRDBCustomSQLite-USER.xcconfig`, add the following entries: + +Append `-D SQLITE_HAS_CODEC -D GRDB_SQLITE_SEE` to your `CUSTOM_OTHER_SWIFT_FLAGS` + +Add the following to the end of the file: +```SQLITE_SEE_PREFIX = /path/to/your/see-code/see-prefix.txt +SQLITE_SEE_CODE = /path/to/your/see-code/see.c +``` + +4. In your `GRDBCustomSQLite-USER.h`, add the following lines: +```#define SQLITE_HAS_CODEC +#define GRDB_SQLITE_SEE +``` + +This allows GRDB to include the needed API around the encryption extension. + +5. You can now use GRDB around the SQLite Encryption Extension by specifying a key in your database configuration From 48efabd382531137af8201d690bd325d938a786c Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Fri, 12 Apr 2019 08:23:25 +0100 Subject: [PATCH 15/17] Fix merge/rebase artifacts --- GRDB/Core/Database.swift | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/GRDB/Core/Database.swift b/GRDB/Core/Database.swift index 2bec17fde5..573fe19e8b 100644 --- a/GRDB/Core/Database.swift +++ b/GRDB/Core/Database.swift @@ -169,7 +169,7 @@ public final class Database { self.sqliteConnection = try Database.openConnection(path: path, flags: configuration.SQLiteOpenFlags) #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE if let key = configuration.key { - try Database.set(key: key, withEncryptionType: configuration.encryptionType, forConnection: sqliteConnection) + try Database.set(key: key, withEncryptionAlgorithm: configuration.encryptionAlgorithm, forConnection: sqliteConnection) } #endif self.configuration = configuration @@ -300,18 +300,6 @@ extension Database { } } #endif - - private static func validateDatabaseFormat(_ sqliteConnection: SQLiteConnection) throws { - // Users are surprised when they open a picture as a database and - // see no error (https://github.com/groue/GRDB.swift/issues/54). - // - // So let's fail early if file is not a database, or encrypted with - // another passphrase. - let code = sqlite3_exec(sqliteConnection, "SELECT * FROM sqlite_master LIMIT 1", nil, nil, nil) - guard code == SQLITE_OK else { - throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) - } - } } extension Database { @@ -329,7 +317,7 @@ extension Database { observationBroker.installCommitAndRollbackHooks() try activateExtendedCodes() - #if SQLITE_HAS_CODEC + #if SQLITE_HAS_CODEC && GRDBCIPHER try validateSQLCipher() if let passphrase = configuration.passphrase { try setCipherPassphrase(passphrase) @@ -454,7 +442,7 @@ extension Database { } } - #if SQLITE_HAS_CODEC + #if SQLITE_HAS_CODEC && GRDBCIPHER private func validateSQLCipher() throws { // https://discuss.zetetic.net/t/important-advisory-sqlcipher-with-xcode-8-and-new-sdks/1688 // From 60f3db5f00e4f05d714dc4f542515f620a45068e Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Fri, 12 Apr 2019 08:31:45 +0100 Subject: [PATCH 16/17] Fix incorrectly failing tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don’t bother testing for error descriptions, these might change. Just check error codes and messages from SQLite as these remain more constant. --- .../SQLiteEncryptionExtensionTests.swift | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift index 08c6fe333e..b8e58996cd 100644 --- a/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift +++ b/Tests/GRDBTests/SQLiteEncryptionExtensionTests.swift @@ -44,8 +44,6 @@ class SQLiteEncryptionExtensionTests: GRDBTestCase { } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) XCTAssertEqual(error.message!, "file is not a database") - XCTAssertTrue(error.sql == nil) - XCTAssertEqual(error.description, "SQLite error 26: file is not a database") } } } @@ -68,8 +66,6 @@ class SQLiteEncryptionExtensionTests: GRDBTestCase { } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) XCTAssertEqual(error.message!, "file is not a database") - XCTAssertTrue(error.sql == nil) - XCTAssertEqual(error.description, "SQLite error 26: file is not a database") } } } @@ -94,8 +90,6 @@ class SQLiteEncryptionExtensionTests: GRDBTestCase { } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) XCTAssertEqual(error.message!, "file is not a database") - XCTAssertTrue(error.sql == nil) - XCTAssertEqual(error.description, "SQLite error 26: file is not a database") } } } @@ -170,8 +164,6 @@ class SQLiteEncryptionExtensionTests: GRDBTestCase { } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) XCTAssertEqual(error.message!, "file is not a database") - XCTAssertTrue(error.sql == nil) - XCTAssertEqual(error.description, "SQLite error 26: file is not a database") } } } @@ -194,8 +186,6 @@ class SQLiteEncryptionExtensionTests: GRDBTestCase { } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) XCTAssertEqual(error.message!, "file is not a database") - XCTAssertTrue(error.sql == nil) - XCTAssertEqual(error.description, "SQLite error 26: file is not a database") } } } @@ -266,8 +256,6 @@ class SQLiteEncryptionExtensionTests: GRDBTestCase { } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) XCTAssertEqual(error.message!, "file is not a database") - XCTAssertTrue(error.sql == nil) - XCTAssertEqual(error.description, "SQLite error 26: file is not a database") } } } @@ -290,8 +278,6 @@ class SQLiteEncryptionExtensionTests: GRDBTestCase { } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) XCTAssertEqual(error.message!, "file is not a database") - XCTAssertTrue(error.sql == nil) - XCTAssertEqual(error.description, "SQLite error 26: file is not a database") } } } @@ -317,8 +303,6 @@ class SQLiteEncryptionExtensionTests: GRDBTestCase { } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) XCTAssertEqual(error.message!, "file is not a database") - XCTAssertTrue(error.sql == nil) - XCTAssertEqual(error.description, "SQLite error 26: file is not a database") } } } @@ -392,8 +376,6 @@ class SQLiteEncryptionExtensionTests: GRDBTestCase { } catch let error as DatabaseError { XCTAssertEqual(error.resultCode, .SQLITE_NOTADB) XCTAssertEqual(error.message!, "file is not a database") - XCTAssertTrue(error.sql == nil) - XCTAssertEqual(error.description, "SQLite error 26: file is not a database") } } } From 4f695f1ab51a6ddfa6e2893870a052115e81671d Mon Sep 17 00:00:00 2001 From: Ben Barnett Date: Sun, 21 Apr 2019 07:13:33 +0100 Subject: [PATCH 17/17] Make SEE code match style of SQLCipher code --- GRDB/Core/Database.swift | 117 +++++++-------------------------------- 1 file changed, 19 insertions(+), 98 deletions(-) diff --git a/GRDB/Core/Database.swift b/GRDB/Core/Database.swift index 573fe19e8b..64d79535ce 100644 --- a/GRDB/Core/Database.swift +++ b/GRDB/Core/Database.swift @@ -167,11 +167,6 @@ public final class Database { init(path: String, configuration: Configuration, schemaCache: DatabaseSchemaCache) throws { self.sqliteConnection = try Database.openConnection(path: path, flags: configuration.SQLiteOpenFlags) - #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE - if let key = configuration.key { - try Database.set(key: key, withEncryptionAlgorithm: configuration.encryptionAlgorithm, forConnection: sqliteConnection) - } - #endif self.configuration = configuration self.schemaCache = schemaCache } @@ -207,99 +202,6 @@ extension Database { } throw DatabaseError(resultCode: .SQLITE_INTERNAL) // WTF SQLite? } - - private static func activateExtendedCodes(_ sqliteConnection: SQLiteConnection) throws { - let code = sqlite3_extended_result_codes(sqliteConnection, 1) - guard code == SQLITE_OK else { - throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) - } - } - - #if SQLITE_HAS_CODEC && GRDBCIPHER - private static func validateSQLCipher(_ sqliteConnection: SQLiteConnection) throws { - // https://discuss.zetetic.net/t/important-advisory-sqlcipher-with-xcode-8-and-new-sdks/1688 - // - // > In order to avoid situations where SQLite might be used - // > improperly at runtime, we strongly recommend that - // > applications institute a runtime test to ensure that the - // > application is actually using SQLCipher on the active - // > connection. - var sqliteStatement: SQLiteStatement? = nil - let code = sqlite3_prepare_v2(sqliteConnection, "PRAGMA cipher_version", -1, &sqliteStatement, nil) - guard code == SQLITE_OK else { - throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) - } - defer { - sqlite3_finalize(sqliteStatement) - } - if sqlite3_step(sqliteStatement) != SQLITE_ROW || (sqlite3_column_text(sqliteStatement, 0) == nil) { - throw DatabaseError(resultCode: .SQLITE_MISUSE, message: """ - GRDB is not linked against SQLCipher. \ - Check https://discuss.zetetic.net/t/important-advisory-sqlcipher-with-xcode-8-and-new-sdks/1688 - """) - } - } - - private static func set(passphrase: String, forConnection sqliteConnection: SQLiteConnection) throws { - let data = passphrase.data(using: .utf8)! - #if swift(>=5.0) - let code = data.withUnsafeBytes { - sqlite3_key(sqliteConnection, $0.baseAddress, Int32($0.count)) - } - #else - let code = data.withUnsafeBytes { - sqlite3_key(sqliteConnection, $0, Int32(data.count)) - } - #endif - guard code == SQLITE_OK else { - throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) - } - } - - private static func set(cipherPageSize: Int, forConnection sqliteConnection: SQLiteConnection) throws { - var sqliteStatement: SQLiteStatement? = nil - var code = sqlite3_prepare_v2(sqliteConnection, "PRAGMA cipher_page_size = \(cipherPageSize)", -1, &sqliteStatement, nil) - guard code == SQLITE_OK else { - throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) - } - defer { - sqlite3_finalize(sqliteStatement) - } - code = sqlite3_step(sqliteStatement) - if code != SQLITE_DONE { - throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) - } - } - - private static func set(kdfIterations: Int, forConnection sqliteConnection: SQLiteConnection) throws { - var sqliteStatement: SQLiteStatement? = nil - var code = sqlite3_prepare_v2(sqliteConnection, "PRAGMA kdf_iter = \(kdfIterations)", -1, &sqliteStatement, nil) - guard code == SQLITE_OK else { - throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) - } - defer { - sqlite3_finalize(sqliteStatement) - } - code = sqlite3_step(sqliteStatement) - if code != SQLITE_DONE { - throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) - } - } - #endif - - // For SQLite-SEE - #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE - private static func set(key: String, withEncryptionAlgorithm algorithm: EncryptionAlgorithm, forConnection sqliteConnection: SQLiteConnection) throws { - let prefixedKey = "\(algorithm.rawValue):\(key)" - let data = prefixedKey.data(using: .utf8)! - let code = data.withUnsafeBytes { - sqlite3_key_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) - } - guard code == SQLITE_OK else { - throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) - } - } - #endif } extension Database { @@ -323,6 +225,12 @@ extension Database { try setCipherPassphrase(passphrase) } #endif + + #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + if let key = configuration.key { + try setEncryptionKey(key, algorithm: configuration.encryptionAlgorithm) + } + #endif // Last step before we can start accessing the database. // This is the opportunity to run SQLCipher configuration @@ -476,6 +384,19 @@ extension Database { } #endif + #if SQLITE_HAS_CODEC && GRDB_SQLITE_SEE + private func setEncryptionKey(_ key: String, algorithm: EncryptionAlgorithm) throws { + let prefixedKey = "\(algorithm.rawValue):\(key)" + let data = prefixedKey.data(using: .utf8)! + let code = data.withUnsafeBytes { + sqlite3_key_v2(sqliteConnection, nil, $0.baseAddress, Int32($0.count)) + } + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: String(cString: sqlite3_errmsg(sqliteConnection))) + } + } + #endif + private func validateFormat() throws { // Users are surprised when they open a picture as a database and // see no error (https://github.com/groue/GRDB.swift/issues/54).