diff --git a/.gitignore b/.gitignore index 7c7e11035d..c06534e1d7 100644 --- a/.gitignore +++ b/.gitignore @@ -115,3 +115,9 @@ Tests/SPM/Packages # Test products Tests/products + +# SPM Package.resolved +**Package.resolved + +# SQLCipher SPM Test project xcworkspace +Tests/SPM/sqlcipher/sqlcipher.xcodeproj/project.xcworkspace/ diff --git a/GRDB/Core/Configuration.swift b/GRDB/Core/Configuration.swift index 933fb9c2b8..e2b1ea2c81 100644 --- a/GRDB/Core/Configuration.swift +++ b/GRDB/Core/Configuration.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Database+SQLCipher.swift b/GRDB/Core/Database+SQLCipher.swift new file mode 100644 index 0000000000..fa031d6259 --- /dev/null +++ b/GRDB/Core/Database+SQLCipher.swift @@ -0,0 +1,211 @@ +#if SQLITE_HAS_CODEC +import SQLCipher + +extension Database { + + /// Granularitly of SQLCipher log outputs + /// Each log level is more verbose than the last + /// + /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log_level + public enum CipherLogLevel: String { + case none + case error + case warn + case info + case debug + case trace + } + + /// - Returns: the SQLCipher version + /// + /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_version + public var cipherVersion: String { + get throws { try String.fetchOne(self, sql: "PRAGMA cipher_version")! } + } + + /// - Returns: the SQLCipher fips status: 1 for fips mode, 0 for non-fips mode + /// The FIPS status will not be initialized until the database connection has been keyed + /// + /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_fips_status + public var cipherFipsStatus: String? { + get throws { try String.fetchOne(self, sql: "PRAGMA cipher_fips_status") } + } + + /// - Returns: The compiled crypto provider. + /// The database must be keyed before requesting the name of the crypto provider. + /// + /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_provider + public var cipherProvider: String? { + get throws { try String.fetchOne(self, sql: "PRAGMA cipher_provider") } + } + + /// - Returns: the version number provided from the compiled crypto provider. + /// This value, if known, is available only after the database has been keyed. + /// + /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_provider_version + public var cipherProviderVersion: String? { + get throws { try String.fetchOne(self, sql: "PRAGMA cipher_provider_version") } + } + + /// Sets the passphrase used to crypt and decrypt an SQLCipher database. + /// + /// Call this method from `Configuration.prepareDatabase`, + /// as in the example below: + /// + /// var config = Configuration() + /// config.prepareDatabase { db in + /// try db.usePassphrase("secret") + /// } + public func usePassphrase(_ passphrase: String) throws { + guard var data = passphrase.data(using: .utf8) else { + throw DatabaseError(message: "invalid passphrase") + } + defer { + data.resetBytes(in: 0.. We (Zetetic) have been discouraging the use of sqlite3_rekey in + // > favor of attaching a new database with the desired encryption + // > options and using sqlcipher_export() to migrate the contents and + // > schema of the original db into the new one: + // > https://discuss.zetetic.net/t/how-to-encrypt-a-plaintext-sqlite-database-to-use-sqlcipher-and-avoid-file-is-encrypted-or-is-not-a-database-errors/ + let code = passphrase.withUnsafeBytes { + sqlite3_rekey(sqliteConnection, $0.baseAddress, CInt($0.count)) + } + guard code == SQLITE_OK else { + throw DatabaseError(resultCode: code, message: lastErrorMessage) + } + } + + /// When using Commercial or Enterprise SQLCipher packages you must call + /// `PRAGMA cipher_license` with a valid license code prior to executing + /// cryptographic operations on an encrypted database. + /// Failure to provide a license code, or use of an expired trial code, + /// will result in an `SQLITE_AUTH (23)` error code reported from the SQLite API + /// License Codes will activate SQLCipher Commercial or Enterprise packages + /// from Zetetic: https://www.zetetic.net/sqlcipher/buy/ + /// 15-day free trials are available by request: https://www.zetetic.net/sqlcipher/trial/ + /// + /// Call this method from `Configuration.prepareDatabase`, + /// as in the example below: + /// + /// var config = Configuration() + /// config.prepareDatabase { db in + /// try db.applyLicense(license) + /// } + /// + /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_license + /// - Parameter license: base64 SQLCipher license code to activate SQLCipher commercial + public func applyLicense(_ license: String) throws { + try execute(sql: "PRAGMA cipher_license = '\(license)'") + } + + /// Instructs SQLCipher to log internal debugging and operational information + /// to the sepecified log target (device) using `os_log` + /// The supplied logLevel will determine the granularity of the logs output + /// Available logLevel options are: NONE, ERROR, WARN, INFO, DEBUG, TRACE + /// Note that each level is more verbose than the last, + /// and particularly with DEBUG and TRACE the logging system will generate + /// a significant log volume + /// + /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log + /// - Parameter logLevel: CipherLogLevel The granularity to use for the logging system - defaults to `DEBUG` + public func enableCipherLogging(logLevel: CipherLogLevel = .debug) throws { + try execute(sql: "PRAGMA cipher_log = device") + try execute(sql: "PRAGMA cipher_log_level = \(logLevel.rawValue.uppercased())") + } + + /// Instructs SQLCipher to disable logging internal debugging and operational information + /// + /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log + public func disableCipherLogging() throws { + try execute(sql: "PRAGMA cipher_log_level = \(CipherLogLevel.none.rawValue.uppercased())") + } + + internal func validateSQLCipher() 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. + if try String.fetchOne(self, sql: "PRAGMA cipher_version") == 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 + """) + } + } + + internal func dropAllDatabaseObjects() throws { + // 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. + + // Prevent foreign keys from messing with drop table statements + let foreignKeysEnabled = try Bool.fetchOne(self, sql: "PRAGMA foreign_keys")! + if foreignKeysEnabled { + try execute(sql: "PRAGMA foreign_keys = OFF") + } + + try throwingFirstError( + execute: { + // Remove all database objects, one after the other + try inTransaction { + let sql = "SELECT type, name FROM sqlite_master WHERE name NOT LIKE 'sqlite_%'" + while let row = try Row.fetchOne(self, sql: sql) { + let type: String = row["type"] + let name: String = row["name"] + try execute(sql: "DROP \(type) \(name.quotedDatabaseIdentifier)") + } + return .commit + } + }, + finally: { + // Restore foreign keys if needed + if foreignKeysEnabled { + try execute(sql: "PRAGMA foreign_keys = ON") + } + }) + } +} + +#endif diff --git a/GRDB/Core/Database+Schema.swift b/GRDB/Core/Database+Schema.swift index 593a1fcd8e..d1e17ca491 100644 --- a/GRDB/Core/Database+Schema.swift +++ b/GRDB/Core/Database+Schema.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Database+Statements.swift b/GRDB/Core/Database+Statements.swift index 34a362b9dc..3f75d47284 100644 --- a/GRDB/Core/Database+Statements.swift +++ b/GRDB/Core/Database+Statements.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Database.swift b/GRDB/Core/Database.swift index 2614602ddd..010cf284b6 100644 --- a/GRDB/Core/Database.swift +++ b/GRDB/Core/Database.swift @@ -4,8 +4,9 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher +import GRDBSQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif @@ -641,24 +642,6 @@ public final class Database: CustomStringConvertible, CustomDebugStringConvertib } } - #if SQLITE_HAS_CODEC - private func validateSQLCipher() 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. - if try String.fetchOne(self, sql: "PRAGMA cipher_version") == 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 - """) - } - } - #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). @@ -1796,35 +1779,7 @@ public final class Database: CustomStringConvertible, CustomDebugStringConvertib func erase() throws { #if SQLITE_HAS_CODEC - // 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. - - // Prevent foreign keys from messing with drop table statements - let foreignKeysEnabled = try Bool.fetchOne(self, sql: "PRAGMA foreign_keys")! - if foreignKeysEnabled { - try execute(sql: "PRAGMA foreign_keys = OFF") - } - - try throwingFirstError( - execute: { - // Remove all database objects, one after the other - try inTransaction { - let sql = "SELECT type, name FROM sqlite_master WHERE name NOT LIKE 'sqlite_%'" - while let row = try Row.fetchOne(self, sql: sql) { - let type: String = row["type"] - let name: String = row["name"] - try execute(sql: "DROP \(type) \(name.quotedDatabaseIdentifier)") - } - return .commit - } - }, - finally: { - // Restore foreign keys if needed - if foreignKeysEnabled { - try execute(sql: "PRAGMA foreign_keys = ON") - } - }) + try dropAllDatabaseObjects() #else try DatabaseQueue().backup(to: self) #endif @@ -1947,80 +1902,6 @@ public final class Database: CustomStringConvertible, CustomDebugStringConvertib @available(*, unavailable) extension Database: Sendable { } -#if SQLITE_HAS_CODEC -extension Database { - - // MARK: - Encryption - - /// Sets the passphrase used to crypt and decrypt an SQLCipher database. - /// - /// Call this method from `Configuration.prepareDatabase`, - /// as in the example below: - /// - /// var config = Configuration() - /// config.prepareDatabase { db in - /// try db.usePassphrase("secret") - /// } - public func usePassphrase(_ passphrase: String) throws { - guard var data = passphrase.data(using: .utf8) else { - throw DatabaseError(message: "invalid passphrase") - } - defer { - data.resetBytes(in: 0.. We (Zetetic) have been discouraging the use of sqlite3_rekey in - // > favor of attaching a new database with the desired encryption - // > options and using sqlcipher_export() to migrate the contents and - // > schema of the original db into the new one: - // > https://discuss.zetetic.net/t/how-to-encrypt-a-plaintext-sqlite-database-to-use-sqlcipher-and-avoid-file-is-encrypted-or-is-not-a-database-errors/ - let code = passphrase.withUnsafeBytes { - sqlite3_rekey(sqliteConnection, $0.baseAddress, CInt($0.count)) - } - guard code == SQLITE_OK else { - throw DatabaseError(resultCode: code, message: lastErrorMessage) - } - } -} -#endif - extension Database { /// Returns the count of changes executed by one statement execution. func countChanges(_ count: inout Int, forTable tableName: String, updates: () throws -> T) throws -> T { diff --git a/GRDB/Core/DatabaseCollation.swift b/GRDB/Core/DatabaseCollation.swift index af59112bab..82f583ab39 100644 --- a/GRDB/Core/DatabaseCollation.swift +++ b/GRDB/Core/DatabaseCollation.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/DatabaseError.swift b/GRDB/Core/DatabaseError.swift index 78151bfca8..f7c6b2c5d6 100644 --- a/GRDB/Core/DatabaseError.swift +++ b/GRDB/Core/DatabaseError.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/DatabaseFunction.swift b/GRDB/Core/DatabaseFunction.swift index 35bc52bd0d..845433a0da 100644 --- a/GRDB/Core/DatabaseFunction.swift +++ b/GRDB/Core/DatabaseFunction.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/DatabaseSnapshotPool.swift b/GRDB/Core/DatabaseSnapshotPool.swift index 4604c9c2cb..460c799f40 100644 --- a/GRDB/Core/DatabaseSnapshotPool.swift +++ b/GRDB/Core/DatabaseSnapshotPool.swift @@ -5,8 +5,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif @@ -140,7 +140,7 @@ public final class DatabaseSnapshotPool { let walSnapshot = try db.isolated(readOnly: true) { try WALSnapshot(db) } - var holderConfig = Configuration() + var holderConfig = configuration holderConfig.allowsUnsafeTransactions = true snapshotHolder = try DatabaseQueue(path: path, configuration: holderConfig) try snapshotHolder.inDatabase { db in @@ -199,7 +199,7 @@ public final class DatabaseSnapshotPool { var configuration = Self.configure(configuration) // Acquire and hold WAL snapshot - var holderConfig = Configuration() + var holderConfig = configuration holderConfig.allowsUnsafeTransactions = true snapshotHolder = try DatabaseQueue(path: path, configuration: holderConfig) let walSnapshot = try snapshotHolder.inDatabase { db in diff --git a/GRDB/Core/DatabaseValue.swift b/GRDB/Core/DatabaseValue.swift index 2459e3ba75..7a36b34772 100644 --- a/GRDB/Core/DatabaseValue.swift +++ b/GRDB/Core/DatabaseValue.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Row.swift b/GRDB/Core/Row.swift index c4b639ae79..8f34f28514 100644 --- a/GRDB/Core/Row.swift +++ b/GRDB/Core/Row.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/RowDecodingError.swift b/GRDB/Core/RowDecodingError.swift index 025bb8035e..b1db07eaac 100644 --- a/GRDB/Core/RowDecodingError.swift +++ b/GRDB/Core/RowDecodingError.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Statement.swift b/GRDB/Core/Statement.swift index 6bf3a89ad4..5f91e013be 100644 --- a/GRDB/Core/Statement.swift +++ b/GRDB/Core/Statement.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/StatementAuthorizer.swift b/GRDB/Core/StatementAuthorizer.swift index a8871726fe..6dc9c4ae51 100644 --- a/GRDB/Core/StatementAuthorizer.swift +++ b/GRDB/Core/StatementAuthorizer.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/StatementColumnConvertible.swift b/GRDB/Core/StatementColumnConvertible.swift index 490dffb7f7..4da0217444 100644 --- a/GRDB/Core/StatementColumnConvertible.swift +++ b/GRDB/Core/StatementColumnConvertible.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Support/Foundation/Data.swift b/GRDB/Core/Support/Foundation/Data.swift index fd7d714a01..ff895b6e72 100644 --- a/GRDB/Core/Support/Foundation/Data.swift +++ b/GRDB/Core/Support/Foundation/Data.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Support/Foundation/DatabaseDateComponents.swift b/GRDB/Core/Support/Foundation/DatabaseDateComponents.swift index d857ecbc24..e7cec16243 100644 --- a/GRDB/Core/Support/Foundation/DatabaseDateComponents.swift +++ b/GRDB/Core/Support/Foundation/DatabaseDateComponents.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Support/Foundation/Date.swift b/GRDB/Core/Support/Foundation/Date.swift index dadc00fd65..a1d179e8fe 100644 --- a/GRDB/Core/Support/Foundation/Date.swift +++ b/GRDB/Core/Support/Foundation/Date.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Support/Foundation/Decimal.swift b/GRDB/Core/Support/Foundation/Decimal.swift index 70d84f3ab7..0be2a68a9a 100644 --- a/GRDB/Core/Support/Foundation/Decimal.swift +++ b/GRDB/Core/Support/Foundation/Decimal.swift @@ -5,8 +5,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Support/Foundation/UUID.swift b/GRDB/Core/Support/Foundation/UUID.swift index 54b18b2c80..f2780c3cc0 100644 --- a/GRDB/Core/Support/Foundation/UUID.swift +++ b/GRDB/Core/Support/Foundation/UUID.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Support/StandardLibrary/Optional.swift b/GRDB/Core/Support/StandardLibrary/Optional.swift index 253b06e53d..b598b119ae 100644 --- a/GRDB/Core/Support/StandardLibrary/Optional.swift +++ b/GRDB/Core/Support/StandardLibrary/Optional.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/Support/StandardLibrary/StandardLibrary.swift b/GRDB/Core/Support/StandardLibrary/StandardLibrary.swift index f967fb8375..c431aecc57 100644 --- a/GRDB/Core/Support/StandardLibrary/StandardLibrary.swift +++ b/GRDB/Core/Support/StandardLibrary/StandardLibrary.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/TransactionObserver.swift b/GRDB/Core/TransactionObserver.swift index 67212aad65..80289983ef 100644 --- a/GRDB/Core/TransactionObserver.swift +++ b/GRDB/Core/TransactionObserver.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Core/WALSnapshot.swift b/GRDB/Core/WALSnapshot.swift index 729a8ac805..7009b4b5e4 100644 --- a/GRDB/Core/WALSnapshot.swift +++ b/GRDB/Core/WALSnapshot.swift @@ -5,8 +5,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Dump/DumpFormats/DebugDumpFormat.swift b/GRDB/Dump/DumpFormats/DebugDumpFormat.swift index f957b1936c..0d0288ce01 100644 --- a/GRDB/Dump/DumpFormats/DebugDumpFormat.swift +++ b/GRDB/Dump/DumpFormats/DebugDumpFormat.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Dump/DumpFormats/JSONDumpFormat.swift b/GRDB/Dump/DumpFormats/JSONDumpFormat.swift index 42dd67dba3..47cb75d36c 100644 --- a/GRDB/Dump/DumpFormats/JSONDumpFormat.swift +++ b/GRDB/Dump/DumpFormats/JSONDumpFormat.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Dump/DumpFormats/LineDumpFormat.swift b/GRDB/Dump/DumpFormats/LineDumpFormat.swift index 5111090e5d..7416b96761 100644 --- a/GRDB/Dump/DumpFormats/LineDumpFormat.swift +++ b/GRDB/Dump/DumpFormats/LineDumpFormat.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Dump/DumpFormats/ListDumpFormat.swift b/GRDB/Dump/DumpFormats/ListDumpFormat.swift index f5536641a1..146eb97856 100644 --- a/GRDB/Dump/DumpFormats/ListDumpFormat.swift +++ b/GRDB/Dump/DumpFormats/ListDumpFormat.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Dump/DumpFormats/QuoteDumpFormat.swift b/GRDB/Dump/DumpFormats/QuoteDumpFormat.swift index c605ae1715..fd67b7de15 100644 --- a/GRDB/Dump/DumpFormats/QuoteDumpFormat.swift +++ b/GRDB/Dump/DumpFormats/QuoteDumpFormat.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/FTS/FTS5.swift b/GRDB/FTS/FTS5.swift index 127a43fef2..adb108cc70 100644 --- a/GRDB/FTS/FTS5.swift +++ b/GRDB/FTS/FTS5.swift @@ -5,8 +5,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/FTS/FTS5CustomTokenizer.swift b/GRDB/FTS/FTS5CustomTokenizer.swift index a209337a6c..63e3912fb9 100644 --- a/GRDB/FTS/FTS5CustomTokenizer.swift +++ b/GRDB/FTS/FTS5CustomTokenizer.swift @@ -5,8 +5,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/FTS/FTS5Tokenizer.swift b/GRDB/FTS/FTS5Tokenizer.swift index 35f3ae2672..41bd62aa92 100644 --- a/GRDB/FTS/FTS5Tokenizer.swift +++ b/GRDB/FTS/FTS5Tokenizer.swift @@ -5,8 +5,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/FTS/FTS5WrapperTokenizer.swift b/GRDB/FTS/FTS5WrapperTokenizer.swift index bda4ed1813..0b355f6b56 100644 --- a/GRDB/FTS/FTS5WrapperTokenizer.swift +++ b/GRDB/FTS/FTS5WrapperTokenizer.swift @@ -5,8 +5,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/GRDB/Record/FetchableRecord+Decodable.swift b/GRDB/Record/FetchableRecord+Decodable.swift index c54ab06bcc..069a06d8fb 100644 --- a/GRDB/Record/FetchableRecord+Decodable.swift +++ b/GRDB/Record/FetchableRecord+Decodable.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/Makefile b/Makefile index 9c91521f3b..0bad2f9d89 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ # make distclean - Restore repository to a pristine state default: test -smokeTest: test_framework_GRDBiOS_maxTarget test_framework_GRDBiOS_minTarget test_framework_SQLCipher3 test_framework_SQLCipher4Encrypted test_framework_GRDBCustomSQLiteiOS_maxTarget test_SPM +smokeTest: test_framework_GRDBiOS_maxTarget test_framework_GRDBiOS_minTarget test_framework_SQLCipher3 test_framework_SQLCipher4Encrypted test_framework_GRDBCustomSQLiteiOS_maxTarget test_SPM test_SPM_SQLCipher # ===== # Tools @@ -74,7 +74,7 @@ endif test: test_framework test_archive test_install test_demo_apps test_framework: test_framework_darwin -test_framework_darwin: test_framework_GRDB test_framework_GRDBCustom test_framework_SQLCipher test_SPM +test_framework_darwin: test_framework_GRDB test_framework_GRDBCustom test_framework_SQLCipher test_SPM test_SPM_SQLCipher test_framework_GRDB: test_framework_GRDBOSX test_framework_GRDBiOS test_framework_GRDBtvOS test_framework_GRDBCustom: test_framework_GRDBCustomSQLiteOSX test_framework_GRDBCustomSQLiteiOS test_framework_SQLCipher: test_framework_SQLCipher3 test_framework_SQLCipher3Encrypted test_framework_SQLCipher4 test_framework_SQLCipher4Encrypted @@ -223,6 +223,13 @@ test_SPM: $(SWIFT) build -c release set -o pipefail && $(SWIFT) test --parallel +test_SPM_SQLCipher: + rm -rf Tests/products + $(SWIFT) package clean + $(SWIFT) build --traits SQLCipher + $(SWIFT) build -c release --traits SQLCipher + set -o pipefail && $(SWIFT) test --parallel --traits SQLCipher + test_universal_xcframework: rm -rf Tests/products mkdir Tests/products @@ -278,7 +285,7 @@ test_install_manual: clean build \ $(XCPRETTY) -test_install_SPM: test_install_SPM_Package test_install_SPM_Project test_install_SPM_Dynamic_Project test_install_SPM_macos_release test_install_SPM_ios_release +test_install_SPM: test_install_SPM_Package test_install_SPM_Project test_install_SPM_Dynamic_Project test_install_SPM_macos_release test_install_SPM_ios_release test_install_SPM_SQLCipher test_install_SPM_Package: rm -rf Tests/products @@ -326,6 +333,30 @@ test_install_SPM_ios_release: clean build \ $(XCPRETTY) +test_install_SPM_SQLCipher: + rm -rf Tests/products + $(XCODEBUILD) \ + -project Tests/SPM/sqlcipher/sqlcipher.xcodeproj \ + -scheme sqlcipher \ + -destination $(MAX_IOS_DESTINATION) \ + -configuration Release \ + clean build \ + $(XCPRETTY) + $(XCODEBUILD) \ + -project Tests/SPM/sqlcipher/sqlcipher.xcodeproj \ + -scheme sqlcipher \ + -destination "platform=macOS" \ + -configuration Release \ + clean build \ + $(XCPRETTY) + $(XCODEBUILD) \ + -project Tests/SPM/sqlcipher/sqlcipher.xcodeproj \ + -scheme sqlcipher \ + -destination $(MAX_TVOS_DESTINATION) \ + -configuration Release \ + clean build \ + $(XCPRETTY) + test_install_customSQLite: SQLiteCustom $(XCODEBUILD) \ -project Tests/CustomSQLite/CustomSQLite.xcodeproj \ diff --git a/Package.swift b/Package.swift index 9d0c79554f..6f3e165013 100644 --- a/Package.swift +++ b/Package.swift @@ -12,6 +12,11 @@ let darwinPlatforms: [Platform] = [ .visionOS, .watchOS, ] + +let sqlcipherTraitTargetCondition: TargetDependencyCondition? = .when(platforms: darwinPlatforms, traits: ["SQLCipher"]) + +let sqlcipherTraitBuildSettingCondition: BuildSettingCondition? = .when(platforms: darwinPlatforms, traits: ["SQLCipher"]) + var swiftSettings: [SwiftSetting] = [ .define("SQLITE_ENABLE_FTS5"), // Until Xcode has proper support for package traits, we must enable @@ -21,9 +26,16 @@ var swiftSettings: [SwiftSetting] = [ // TODO: when Xcode support traits, remove all mentions of SQLITE_DISABLE_SNAPSHOT and update as below: // .define("SQLITE_ENABLE_SNAPSHOT", .when(platforms: darwinPlatforms, traits: ["GRDBSQLite"])), .define("SQLITE_ENABLE_SNAPSHOT"), + .define("SQLITE_HAS_CODEC", sqlcipherTraitBuildSettingCondition), +] + +var cSettings: [CSetting] = [ + .define("SQLITE_HAS_CODEC", to: nil, sqlcipherTraitBuildSettingCondition) +] + +var dependencies: [PackageDescription.Package.Dependency] = [ + .package(url: "https://github.com/sqlcipher/SQLCipher.swift.git", from: "4.11.0") ] -var cSettings: [CSetting] = [] -var dependencies: [PackageDescription.Package.Dependency] = [] // Don't rely on those environment variables. They are ONLY testing conveniences: // $ SQLITE_ENABLE_PREUPDATE_HOOK=1 make test_SPM @@ -58,6 +70,7 @@ let package = Package( ], traits: [ "GRDBSQLite", + .trait(name: "SQLCipher", description: "Enables SQLCipher encryption when a passphrase is supplied to Database"), .default(enabledTraits: ["GRDBSQLite"]), ], dependencies: dependencies, @@ -65,15 +78,25 @@ let package = Package( .systemLibrary( name: "GRDBSQLite", providers: [.apt(["libsqlite3-dev"])]), + .target( + name: "GRDBSQLCipher", + dependencies: [.product(name: "SQLCipher", package: "SQLCipher.swift")] + ), .target( name: "GRDB", dependencies: [ .target(name: "GRDBSQLite", condition: .when(traits: ["GRDBSQLite"])), + .product(name: "SQLCipher", package: "SQLCipher.swift", condition: sqlcipherTraitTargetCondition), + .target( + name: "GRDBSQLCipher", + condition: sqlcipherTraitTargetCondition + ) ], path: "GRDB", resources: [.copy("PrivacyInfo.xcprivacy")], cSettings: cSettings, - swiftSettings: swiftSettings), + swiftSettings: swiftSettings +), .testTarget( name: "GRDBTests", dependencies: ["GRDB"], @@ -95,6 +118,7 @@ let package = Package( .copy("GRDBTests/Betty.jpeg"), .copy("GRDBTests/InflectionsTests.json"), .copy("GRDBTests/Issue1383.sqlite"), + .copy("GRDBTests/db.SQLCipher3") ], cSettings: cSettings, swiftSettings: swiftSettings + [ @@ -102,7 +126,12 @@ let package = Package( .swiftLanguageMode(.v5), .enableUpcomingFeature("InferSendableFromCaptures"), .enableUpcomingFeature("GlobalActorIsolatedTypesUsability"), - ]) + .define( + "GRDBCIPHER_USE_ENCRYPTION", + sqlcipherTraitBuildSettingCondition + ) + ] +) ], swiftLanguageModes: [.v6] ) diff --git a/README.md b/README.md index 4261c65e8f..c16133c385 100644 --- a/README.md +++ b/README.md @@ -4597,7 +4597,64 @@ try Player.customRequest().fetchAll(db) // [Player] Encryption ========== -**GRDB can encrypt your database with [SQLCipher](http://sqlcipher.net) v3.4+.** +**GRDB can encrypt your database with [SQLCipher](https://www.zetetic.net/sqlcipher) v3.4+.** + +### Swift Package Manager Integration (SQLCipher v4.11+) + +Use the `GRDB.swift` Swift Package and specify the `SQLCipher` trait: + +```swift +depdencies: [ + .package(url: "https://github.com/groue/GRDB.swift.git", from: "7.9.0", traits: ["SQLCipher"]) +] +``` + +As of Xcode 16.4 (16F6), there's no direct way in the Xcode UI to select trait variations so you'll need to use a local wrapper package to pull in the GRDB dependency with the `SQLCipher` trait enabled: + +```swift +// swift-tools-version: 6.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "AppDependencies", + platforms: [ + .macOS(.v10_15), + .iOS(.v13), + .watchOS(.v7), + .tvOS(.v13) + ], + products: [ + .library( + name: "AppDependencies", + targets: ["AppDependencies"]), + ], + dependencies: [ + .package( + url: "https://github.com/groue/GRDB.swift.git", + from: "7.9.0", + traits: ["SQLCipher"]) + ], + targets: [ + .target( + name: "AppDependencies", + dependencies: [ + .product( + name: "GRDB", + package: "GRDB.swift") + ] + ) + ] +) +``` + +Within Xcode add your local `AppDependencies` wrapper package as a package dependency and GRDB with SQLCipher functionality will be accessible. + +### CocoaPods (SQLCipher v3.4-v4.10) + +> [!WARNING] +> CocoaPods has transitioned to [read-only maintainence mode](https://blog.cocoapods.org/CocoaPods-Specs-Repo/) and is scheduled to be deprecated from GRDB Use [CocoaPods](http://cocoapods.org/), and specify in your `Podfile`: @@ -4616,6 +4673,8 @@ Make sure you remove any existing `pod 'GRDB.swift'` from your Podfile. `GRDB.sw - [Creating or Opening an Encrypted Database](#creating-or-opening-an-encrypted-database) - [Changing the Passphrase of an Encrypted Database](#changing-the-passphrase-of-an-encrypted-database) - [Exporting a Database to an Encrypted Database](#exporting-a-database-to-an-encrypted-database) +- [SQLCipher Logging](#enabling-sqlcipher-logging) +- [SQLCipher Information Accessors](#sqlcipher-information-accessors) - [Security Considerations](#security-considerations) @@ -4736,6 +4795,123 @@ try existingDBQueue.inDatabase { db in // Now the export is completed, and the existing database can be deleted. ``` +### SQLCipher Logging + +To instruct SQLCipher to log internal debugging and operational information, use the convenience method `enableCipherLogging(logLevel:)`. + +Logs will be output to the target device using `os_log`. + +The supplied logLevel will determine the granularity of the logs output. Available logLevels are: + +``` +none +error +warn +info +debug +trace +``` + +Each level will be more verbose than the last, and particularly with debug and trace the logging system will generate significant log volume. + +If `enableCipherLogging(logLevel:)` is called without supplying a logLevel, the default logLevel of `debug` will be used. + +This convenience method only logs to the target device, but it's also possible to log to a file using `PRAGMA cipher_log = ;` directly. Please see the [SQLCipher API docs](https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_log) for additional information. + +Enable cipher logging when first setting up the db configuration or after it is setup: + +``` +var config = Configuration() +config.prepareDatabase { db in + try db.enableCipherLogging() + try db.usePassphrase("secret") +} + +let dbQueue = try DatabaseQueue(path: NSTemporaryDirectory().appending("test.db"), configuration: config) +``` + +
+ Example output: + +``` +DEBUG CORE sqlite3_key: db=104E22830 +DEBUG CORE sqlite3_key_v2: db=104E22830 zDb=main db_index=0 +DEBUG CORE sqlcipherCodecAttach: db=104E22830, nDb=0 +DEBUG MEMORY sqlcipher_codec_ctx_init: allocating context +DEBUG MEMORY sqlcipher_codec_ctx_init: allocating kdf_salt +DEBUG MEMORY sqlcipher_codec_ctx_init: allocating hmac_kdf_salt +DEBUG CORE sqlcipher_codec_ctx_reserve_setup: base_reserve=16 block_sz=16 md_size=64 reserve=80 +DEBUG CORE sqlcipher_codec_ctx_reserve_setup: base_reserve=16 block_sz=16 md_size=64 reserve=80 +DEBUG MEMORY sqlcipher_cipher_ctx_init: allocating context +DEBUG MEMORY sqlcipher_cipher_ctx_init: allocating key +DEBUG MEMORY sqlcipher_cipher_ctx_init: allocating hmac_key +DEBUG MEMORY sqlcipher_cipher_ctx_init: allocating context +DEBUG MEMORY sqlcipher_cipher_ctx_init: allocating key +DEBUG MEMORY sqlcipher_cipher_ctx_init: allocating hmac_key +DEBUG CORE sqlcipher_cipher_ctx_copy: target=128009220, source=128009190 +DEBUG CORE sqlcipherCodecAttach: calling sqlcipherPagerSetCodec() +DEBUG CORE codec_set_btree_to_codec_pagesize: sqlite3BtreeSetPageSize() size=4096 reserve=80 +DEBUG CORE codec_set_btree_to_codec_pagesize: sqlite3BtreeSetPageSize returned 0 +DEBUG CORE sqlcipherCodecAttach: calling sqlite3BtreeSecureDelete() +DEBUG CORE sqlcipherCodecAttach: calling sqlite3BtreeSetAutoVacuum() +DEBUG MEMORY codec_ctx_free: iCtx=7FF7BC080088 +DEBUG MEMORY cipher_ctx_free: iCtx=128008110 +DEBUG MEMORY cipher_ctx_free: iCtx=128008118 +``` + +
+ +You can disable cipher logging with `disableCipherLogging()`, following up on the previous example: + +``` +try dbQueue.inDatabase { db in + try db.disableCipherLogging() +} +``` + +### SQLCipher Information Accessors + +GRDB provides convenience accessors to get information about the SQLCipher version, the provider, the provider version, and the fips status. + +`cipherVersion`: Returns the SQLCipher version +- https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_version + +``` +let cipherVersion = try dbQueue.read { db in + try db.cipherVersion +} +``` + +`cipherProvider`: Returns the compiled crypto provider. The database must be keeyed before requesting the name of the crypto provider. +- https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_provider + +``` +let cipherProvider = try dbQueue.read { db in + try db.cipherProvider +} +``` + +`cipherProviderVersion`: Returns the version number provided from the compiled crypto provider. This value, if known, is available only after the database has been keyed. +- https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_provider_version + +``` +let cipherProviderVersion = try dbQueue.read { db in + try db.cipherProviderVersion +} +``` + +`cipherFipsStatus`: Returns the SQLCipher fips status: 1 for fips mode, 0 for non-fips mode. The FIPS status will not be initialized until the database connection has been keyed. +- https://www.zetetic.net/sqlcipher/sqlcipher-api/#cipher_fips_status + +``` +let cipherFipsStatus = try dbQueue.read { db in + try db.cipherFipsStatus +} +``` + + + + ### Security Considerations diff --git a/Scripts/swiftlint.yml b/Scripts/swiftlint.yml index c8a223f0b8..6f6b7cdbd9 100644 --- a/Scripts/swiftlint.yml +++ b/Scripts/swiftlint.yml @@ -5,6 +5,7 @@ disabled_rules: - comment_spacing - cyclomatic_complexity - duplicate_enum_cases + - duplicate_imports - file_length - for_where - force_cast @@ -62,7 +63,6 @@ opt_in_rules: - redundant_type_annotation - shorthand_optional_binding - sorted_first_last - - sorted_imports - toggle_bool - unhandled_throwing_task - vertical_parameter_alignment_on_call diff --git a/Sources/GRDBSQLCipher/SQLCipher_config.c b/Sources/GRDBSQLCipher/SQLCipher_config.c new file mode 100644 index 0000000000..26b22bdb9b --- /dev/null +++ b/Sources/GRDBSQLCipher/SQLCipher_config.c @@ -0,0 +1 @@ +// empty c-file diff --git a/Sources/GRDBSQLCipher/include/SQLCipher_config.h b/Sources/GRDBSQLCipher/include/SQLCipher_config.h new file mode 100644 index 0000000000..b19032d087 --- /dev/null +++ b/Sources/GRDBSQLCipher/include/SQLCipher_config.h @@ -0,0 +1,27 @@ +#ifndef grdb_config_h +#define grdb_config_h + +#include + +typedef void(*_errorLogCallback)(void *pArg, int iErrCode, const char *zMsg); + +/// Wrapper around sqlite3_config(SQLITE_CONFIG_LOG, ...) which is a variadic +/// function that can't be used from Swift. +static inline void _registerErrorLogCallback(_errorLogCallback callback) { + sqlite3_config(SQLITE_CONFIG_LOG, callback, 0); +} + +/// Wrapper around sqlite3_db_config() which is a variadic function that can't +/// be used from Swift. +static inline void _disableDoubleQuotedStringLiterals(sqlite3 *db) { + sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 0, (void *)0); + sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 0, (void *)0); +} + +/// Wrapper around sqlite3_db_config() which is a variadic function that can't +/// be used from Swift. +static inline void _enableDoubleQuotedStringLiterals(sqlite3 *db) { + sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DDL, 1, (void *)0); + sqlite3_db_config(db, SQLITE_DBCONFIG_DQS_DML, 1, (void *)0); +} +#endif /* grdb_config_h */ diff --git a/Sources/GRDBSQLCipher/include/module.modulemap b/Sources/GRDBSQLCipher/include/module.modulemap new file mode 100644 index 0000000000..9dc826c40b --- /dev/null +++ b/Sources/GRDBSQLCipher/include/module.modulemap @@ -0,0 +1,4 @@ +module GRDBSQLCipher { + header "SQLCipher_config.h" + export * +} diff --git a/Tests/GRDBTests/AssociationPrefetchingRowTests.swift b/Tests/GRDBTests/AssociationPrefetchingRowTests.swift index c6fd39bd8a..76ec55d071 100644 --- a/Tests/GRDBTests/AssociationPrefetchingRowTests.swift +++ b/Tests/GRDBTests/AssociationPrefetchingRowTests.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/Tests/GRDBTests/DataMemoryTests.swift b/Tests/GRDBTests/DataMemoryTests.swift index 1187ed2504..5d7f4f5406 100644 --- a/Tests/GRDBTests/DataMemoryTests.swift +++ b/Tests/GRDBTests/DataMemoryTests.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/Tests/GRDBTests/DatabaseConfigurationTests.swift b/Tests/GRDBTests/DatabaseConfigurationTests.swift index f0739ba1c7..e89018492d 100644 --- a/Tests/GRDBTests/DatabaseConfigurationTests.swift +++ b/Tests/GRDBTests/DatabaseConfigurationTests.swift @@ -12,24 +12,32 @@ class DatabaseConfigurationTests: GRDBTestCase { connectionCountMutex.increment() } + connectionCountMutex.store(0) _ = try DatabaseQueue(configuration: configuration) XCTAssertEqual(connectionCountMutex.load(), 1) + connectionCountMutex.store(0) _ = try makeDatabaseQueue(configuration: configuration) - XCTAssertEqual(connectionCountMutex.load(), 2) + XCTAssertEqual(connectionCountMutex.load(), 1) + connectionCountMutex.store(0) let pool = try makeDatabasePool(configuration: configuration) - XCTAssertEqual(connectionCountMutex.load(), 3) + XCTAssertEqual(connectionCountMutex.load(), 1) // writer + connectionCountMutex.store(0) try pool.read { _ in } - XCTAssertEqual(connectionCountMutex.load(), 4) + XCTAssertEqual(connectionCountMutex.load(), 1) // 1st reader + connectionCountMutex.store(0) try pool.makeSnapshot().read { _ in } - XCTAssertEqual(connectionCountMutex.load(), 5) + XCTAssertEqual(connectionCountMutex.load(), 1) // snapshot #if SQLITE_ENABLE_SNAPSHOT && !SQLITE_DISABLE_SNAPSHOT - try pool.makeSnapshotPool().read { _ in } - XCTAssertEqual(connectionCountMutex.load(), 6) + connectionCountMutex.store(0) + let snapshotPool = try pool.makeSnapshotPool() + XCTAssertEqual(connectionCountMutex.load(), 1) // snapshot + try snapshotPool.read { _ in } + XCTAssertEqual(connectionCountMutex.load(), 2) // 1st reader #endif } diff --git a/Tests/GRDBTests/DatabasePoolReleaseMemoryTests.swift b/Tests/GRDBTests/DatabasePoolReleaseMemoryTests.swift index 7302e2aa42..67108860e9 100644 --- a/Tests/GRDBTests/DatabasePoolReleaseMemoryTests.swift +++ b/Tests/GRDBTests/DatabasePoolReleaseMemoryTests.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/Tests/GRDBTests/DatabasePoolTests.swift b/Tests/GRDBTests/DatabasePoolTests.swift index f8192e58f8..86dfa9a757 100644 --- a/Tests/GRDBTests/DatabasePoolTests.swift +++ b/Tests/GRDBTests/DatabasePoolTests.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/Tests/GRDBTests/EncryptionTests.swift b/Tests/GRDBTests/EncryptionTests.swift index a3a1c74536..b101d3ebd5 100644 --- a/Tests/GRDBTests/EncryptionTests.swift +++ b/Tests/GRDBTests/EncryptionTests.swift @@ -4,6 +4,12 @@ import GRDB class EncryptionTests: GRDBTestCase { +#if SWIFT_PACKAGE +let testBundle = Bundle.module +#else +let testBundle = Bundle(for: GRDBTestCase.self) +#endif + func testDatabaseQueueWithPassphraseToDatabaseQueueWithPassphrase() throws { do { var config = Configuration() @@ -707,7 +713,6 @@ class EncryptionTests: GRDBTestCase { else { XCTFail("Unknown SQLCipher version"); return } if cipherMajorVersion >= 4 { - let testBundle = Bundle(for: type(of: self)) let path = testBundle.url(forResource: "db", withExtension: "SQLCipher3")!.path var configuration = Configuration() configuration.prepareDatabase { db in @@ -728,5 +733,48 @@ class EncryptionTests: GRDBTestCase { } } } + + func testCipherVersion() throws { + try DatabaseQueue().inDatabase { db in + _ = try db.cipherVersion + } + } + + func testCipherFipsStatus() throws { + var config = Configuration() + config.prepareDatabase { db in + try db.usePassphrase("secret") + } + let dbQueue = try makeDatabaseQueue(configuration: config) + try dbQueue.inDatabase { db in + XCTAssertEqual("0", try db.cipherFipsStatus) + } + } + + func testCipherProvider() throws { + var config = Configuration() + config.prepareDatabase { db in + try db.usePassphrase("secret") + } + let dbQueue = try makeDatabaseQueue(configuration: config) + try dbQueue.inDatabase { db in + XCTAssertEqual("commoncrypto", try db.cipherProvider) + } + } + + func testCipherProviderVersion() throws { + var config = Configuration() + config.prepareDatabase { db in + try db.usePassphrase("secret") + } + let dbQueue = try makeDatabaseQueue(configuration: config) + let cipherVersion = try dbQueue.read { try $0.cipherVersion } + if "4.10.0".compare(cipherVersion, options: .numeric) == .orderedDescending { + throw XCTSkip("cipher_provider_version isn't available until SQLCipher 4.10.0") + } + try dbQueue.inDatabase { db in + XCTAssertNotNil(try db.cipherProviderVersion) + } + } } #endif diff --git a/Tests/GRDBTests/GRDBTestCase.swift b/Tests/GRDBTests/GRDBTestCase.swift index af1df612be..79d8372ac1 100644 --- a/Tests/GRDBTests/GRDBTestCase.swift +++ b/Tests/GRDBTests/GRDBTestCase.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/Tests/GRDBTests/StatementColumnConvertibleFetchTests.swift b/Tests/GRDBTests/StatementColumnConvertibleFetchTests.swift index 02b77ec50c..af9692cc06 100644 --- a/Tests/GRDBTests/StatementColumnConvertibleFetchTests.swift +++ b/Tests/GRDBTests/StatementColumnConvertibleFetchTests.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/Tests/GRDBTests/UpdateStatementTests.swift b/Tests/GRDBTests/UpdateStatementTests.swift index f11e351176..2f6a90cdcb 100644 --- a/Tests/GRDBTests/UpdateStatementTests.swift +++ b/Tests/GRDBTests/UpdateStatementTests.swift @@ -4,8 +4,8 @@ import SQLCipher #elseif GRDBFRAMEWORK // GRDB.xcodeproj or CocoaPods (standard subspec) import SQLite3 #elseif GRDBCUSTOMSQLITE // GRDBCustom Framework -// #elseif SomeTrait -// import ... +#elseif SQLCipher +import SQLCipher #else // Default SPM trait must be the default. It impossible to detect from Xcode. import GRDBSQLite #endif diff --git a/Tests/GRDBTests/db.SQLCipher3 b/Tests/GRDBTests/db.SQLCipher3 new file mode 100644 index 0000000000..6a6fed7db1 Binary files /dev/null and b/Tests/GRDBTests/db.SQLCipher3 differ diff --git a/Tests/Performance/GRDBPerformance/GRDBPerformance.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Tests/Performance/GRDBPerformance/GRDBPerformance.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 95d315f336..0000000000 --- a/Tests/Performance/GRDBPerformance/GRDBPerformance.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,42 +0,0 @@ -{ - "originHash" : "8d182a38fc35b0d50c198e0cbd39dc7fa5922eae5336ff3b0c73c2d51bcba752", - "pins" : [ - { - "identity" : "fmdb", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ccgus/fmdb.git", - "state" : { - "revision" : "1227a3fa2b9916bfd75fe380eb45cd210e69e251", - "version" : "2.7.12" - } - }, - { - "identity" : "realm-core", - "kind" : "remoteSourceControl", - "location" : "https://github.com/realm/realm-core.git", - "state" : { - "revision" : "15493076ad9fef22c16cc64cbfbf9e5b65c385f9", - "version" : "20.1.0" - } - }, - { - "identity" : "realm-swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/realm/realm-swift.git", - "state" : { - "revision" : "bae6c4be7df169fdb047d0ad63f902c1e2665e83", - "version" : "20.0.1" - } - }, - { - "identity" : "sqlite.swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/stephencelis/SQLite.swift.git", - "state" : { - "revision" : "a95fc6df17d108bd99210db5e8a9bac90fe984b8", - "version" : "0.15.3" - } - } - ], - "version" : 3 -} diff --git a/Tests/SPM/sqlcipher/AppDependencies/.gitignore b/Tests/SPM/sqlcipher/AppDependencies/.gitignore new file mode 100644 index 0000000000..0023a53406 --- /dev/null +++ b/Tests/SPM/sqlcipher/AppDependencies/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Tests/SPM/sqlcipher/AppDependencies/Package.swift b/Tests/SPM/sqlcipher/AppDependencies/Package.swift new file mode 100644 index 0000000000..1cf151e41c --- /dev/null +++ b/Tests/SPM/sqlcipher/AppDependencies/Package.swift @@ -0,0 +1,34 @@ +// swift-tools-version: 6.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "AppDependencies", + platforms: [ + .macOS(.v10_15), + .iOS(.v13), + .watchOS(.v7), + .tvOS(.v13) + ], + products: [ + .library( + name: "AppDependencies", + targets: ["AppDependencies"]), + ], + dependencies: [ + .package( + path: "../../../..", + traits: ["SQLCipher"]) + ], + targets: [ + .target( + name: "AppDependencies", + dependencies: [ + .product( + name: "GRDB", + package: "GRDB.swift") + ] + ) + ] +) diff --git a/Tests/SPM/sqlcipher/AppDependencies/Sources/AppDependencies/AppDependencies.swift b/Tests/SPM/sqlcipher/AppDependencies/Sources/AppDependencies/AppDependencies.swift new file mode 100644 index 0000000000..08b22b80fc --- /dev/null +++ b/Tests/SPM/sqlcipher/AppDependencies/Sources/AppDependencies/AppDependencies.swift @@ -0,0 +1,2 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book diff --git a/Tests/SPM/sqlcipher/sqlcipher.xcodeproj/project.pbxproj b/Tests/SPM/sqlcipher/sqlcipher.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..39eb124ca6 --- /dev/null +++ b/Tests/SPM/sqlcipher/sqlcipher.xcodeproj/project.pbxproj @@ -0,0 +1,398 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 1C4FE57B2E9D909E00C066A8 /* AppDependencies in Frameworks */ = {isa = PBXBuildFile; productRef = 1C4FE57A2E9D909E00C066A8 /* AppDependencies */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1C7EBC072E9D8ED60059C430 /* sqlcipher.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = sqlcipher.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1CFE150A2E9D928F00D98E29 /* sqlcipher.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = sqlcipher.xcodeproj; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 1C7EBC092E9D8ED60059C430 /* sqlcipher */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = sqlcipher; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1C7EBC042E9D8ED60059C430 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1C4FE57B2E9D909E00C066A8 /* AppDependencies in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1C7EBBFE2E9D8ED60059C430 = { + isa = PBXGroup; + children = ( + 1C7EBC092E9D8ED60059C430 /* sqlcipher */, + 1C7EBC082E9D8ED60059C430 /* Products */, + ); + sourceTree = ""; + }; + 1C7EBC082E9D8ED60059C430 /* Products */ = { + isa = PBXGroup; + children = ( + 1C7EBC072E9D8ED60059C430 /* sqlcipher.app */, + ); + name = Products; + sourceTree = ""; + }; + 1CFE150C2E9D928F00D98E29 /* Products */ = { + isa = PBXGroup; + children = ( + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1C7EBC062E9D8ED60059C430 /* sqlcipher */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1C7EBC122E9D8ED90059C430 /* Build configuration list for PBXNativeTarget "sqlcipher" */; + buildPhases = ( + 1C7EBC032E9D8ED60059C430 /* Sources */, + 1C7EBC042E9D8ED60059C430 /* Frameworks */, + 1C7EBC052E9D8ED60059C430 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 1C7EBC092E9D8ED60059C430 /* sqlcipher */, + ); + name = sqlcipher; + packageProductDependencies = ( + 1C4FE57A2E9D909E00C066A8 /* AppDependencies */, + 1CFE15152E9D92A900D98E29 /* AppDependencies */, + ); + productName = sqlcipher; + productReference = 1C7EBC072E9D8ED60059C430 /* sqlcipher.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1C7EBBFF2E9D8ED60059C430 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2600; + LastUpgradeCheck = 2600; + TargetAttributes = { + 1C7EBC062E9D8ED60059C430 = { + CreatedOnToolsVersion = 26.0.1; + }; + }; + }; + buildConfigurationList = 1C7EBC022E9D8ED60059C430 /* Build configuration list for PBXProject "sqlcipher" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 1C7EBBFE2E9D8ED60059C430; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 1CFE15142E9D92A900D98E29 /* XCLocalSwiftPackageReference "AppDependencies" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 1C7EBC082E9D8ED60059C430 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 1CFE150C2E9D928F00D98E29 /* Products */; + ProjectRef = 1CFE150A2E9D928F00D98E29 /* sqlcipher.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 1C7EBC062E9D8ED60059C430 /* sqlcipher */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1C7EBC052E9D8ED60059C430 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1C7EBC032E9D8ED60059C430 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1C7EBC102E9D8ED90059C430 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 1C7EBC112E9D8ED90059C430 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + 1C7EBC132E9D8ED90059C430 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_APP_SANDBOX = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readonly; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 11.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.github.groue.sqlcipher; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SDKROOT = auto; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; + TVOS_DEPLOYMENT_TARGET = 15.6; + XROS_DEPLOYMENT_TARGET = 1.3; + }; + name = Debug; + }; + 1C7EBC142E9D8ED90059C430 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + ENABLE_APP_SANDBOX = YES; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SELECTED_FILES = readonly; + GENERATE_INFOPLIST_FILE = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphoneos*]" = YES; + "INFOPLIST_KEY_UILaunchScreen_Generation[sdk=iphonesimulator*]" = YES; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphoneos*]" = UIStatusBarStyleDefault; + "INFOPLIST_KEY_UIStatusBarStyle[sdk=iphonesimulator*]" = UIStatusBarStyleDefault; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; + LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; + MACOSX_DEPLOYMENT_TARGET = 11.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.github.groue.sqlcipher; + PRODUCT_NAME = "$(TARGET_NAME)"; + REGISTER_APP_GROUPS = YES; + SDKROOT = auto; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx xros xrsimulator"; + SUPPORTS_MACCATALYST = NO; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3,7"; + TVOS_DEPLOYMENT_TARGET = 15.6; + XROS_DEPLOYMENT_TARGET = 1.3; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1C7EBC022E9D8ED60059C430 /* Build configuration list for PBXProject "sqlcipher" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1C7EBC102E9D8ED90059C430 /* Debug */, + 1C7EBC112E9D8ED90059C430 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1C7EBC122E9D8ED90059C430 /* Build configuration list for PBXNativeTarget "sqlcipher" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1C7EBC132E9D8ED90059C430 /* Debug */, + 1C7EBC142E9D8ED90059C430 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 1CFE15142E9D92A900D98E29 /* XCLocalSwiftPackageReference "AppDependencies" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = AppDependencies; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 1C4FE57A2E9D909E00C066A8 /* AppDependencies */ = { + isa = XCSwiftPackageProductDependency; + productName = AppDependencies; + }; + 1CFE15152E9D92A900D98E29 /* AppDependencies */ = { + isa = XCSwiftPackageProductDependency; + productName = AppDependencies; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 1C7EBBFF2E9D8ED60059C430 /* Project object */; +} diff --git a/Tests/SPM/sqlcipher/sqlcipher/Assets.xcassets/AccentColor.colorset/Contents.json b/Tests/SPM/sqlcipher/sqlcipher/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000000..eb87897008 --- /dev/null +++ b/Tests/SPM/sqlcipher/sqlcipher/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tests/SPM/sqlcipher/sqlcipher/Assets.xcassets/AppIcon.appiconset/Contents.json b/Tests/SPM/sqlcipher/sqlcipher/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..ffdfe150be --- /dev/null +++ b/Tests/SPM/sqlcipher/sqlcipher/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,85 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tests/SPM/sqlcipher/sqlcipher/Assets.xcassets/Contents.json b/Tests/SPM/sqlcipher/sqlcipher/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/Tests/SPM/sqlcipher/sqlcipher/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Tests/SPM/sqlcipher/sqlcipher/ContentView.swift b/Tests/SPM/sqlcipher/sqlcipher/ContentView.swift new file mode 100644 index 0000000000..6aaaccf97c --- /dev/null +++ b/Tests/SPM/sqlcipher/sqlcipher/ContentView.swift @@ -0,0 +1,19 @@ +import SwiftUI +import GRDB + +struct ContentView: View { + var body: some View { + VStack { + Text("SQLCipher version: \(cipherVersion)") + } + .padding() + } + + private var cipherVersion: String { + try! DatabaseQueue().read { try $0.cipherVersion } + } +} + +#Preview { + ContentView() +} diff --git a/Tests/SPM/sqlcipher/sqlcipher/sqlcipherApp.swift b/Tests/SPM/sqlcipher/sqlcipher/sqlcipherApp.swift new file mode 100644 index 0000000000..aec640930c --- /dev/null +++ b/Tests/SPM/sqlcipher/sqlcipher/sqlcipherApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct sqlcipherApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +}