diff --git a/.gitignore b/.gitignore index 71c75f9..f6fba90 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,5 @@ ios/fastlane/.env.default ios/android/.env.default ios/Flutter/KeeVault-generated.xcconfig ios/fastlane/report.xml +ios/Runner.app.dSYM.zip +ios/Runner.ipa diff --git a/.vscode/launch.json b/.vscode/launch.json index b1dcc3b..ec986ef 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -60,6 +60,18 @@ "--dart-define", "KEEVAULT_CHANNEL=dev" ] + }, + { + "name": "keevault release local device", + "request": "launch", + "type": "dart", + "flutterMode": "release", + "args": [ + "--dart-define", + "KEEVAULT_STAGE=", + "--dart-define", + "KEEVAULT_CHANNEL=play" + ] } ] } \ No newline at end of file diff --git a/ios/KdbxSwift/Sources/KdbxSwift/DatabaseFileManager.swift b/ios/KdbxSwift/Sources/KdbxSwift/DatabaseFileManager.swift index 0847f46..307563b 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/DatabaseFileManager.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/DatabaseFileManager.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class DatabaseFileManager { public enum Error { @@ -32,6 +33,7 @@ public class DatabaseFileManager { sharedGroupName: String, sharedDefaults: UserDefaults ) { + Logger.mainLog.debug("DatabaseFileManager.init started") self.preTransformedKeyMaterial = preTransformedKeyMaterial.clone() self.status = status @@ -43,10 +45,10 @@ public class DatabaseFileManager { private func initDatabase(signature data: ByteArray) -> Database? { if Database2.isSignatureMatches(data: data) { - Diag.info("DB signature: KDBX") + Logger.mainLog.info("DB signature: KDBX") return Database2() } else { - Diag.info("DB signature: no match") + Logger.mainLog.info("DB signature: no match") return nil } } @@ -58,19 +60,21 @@ public class DatabaseFileManager { do { let autofillFileData = try ByteArray(contentsOf: kdbxAutofillURL, options: [.uncached, .mappedIfSafe]) fileData = autofillFileData + Logger.mainLog.debug("Loaded autofill KDBX") } catch { - Diag.info("Autofill file not found. Expected unless recent changes have been made via autofill and main app not opened yet.") + Logger.mainLog.info("Autofill file not found. Expected unless recent changes have been made via autofill and main app not opened yet.") do { fileData = try ByteArray(contentsOf: kdbxCurrentURL, options: [.uncached, .mappedIfSafe]) + Logger.mainLog.debug("Loaded main KDBX") } catch { - Diag.error("Failed to read current KDBX file [message: \(error.localizedDescription)]") - fatalError("couldn't read KDBX file") + Logger.mainLog.error("Failed to read current KDBX file [message: \(error.localizedDescription, privacy: .public)]") + Logger.fatalError("couldn't read KDBX file") } } guard let db = initDatabase(signature: fileData) else { - fatalError("database init failed") + Logger.fatalError("database init failed") } let dbFile = DatabaseFile( @@ -89,20 +93,21 @@ public class DatabaseFileManager { database = db } catch { - fatalError("Unprocessed exception while opening database. Possibly hardware failure has corrupted the data on this device.") + Logger.fatalError("Unprocessed exception while opening database. Possibly hardware failure has corrupted the data on this device.") } return dbFile } public func saveToFile(db: Database?) { do { + Logger.mainLog.debug("Saving to autofill KDBX") guard let targetDatabase = db ?? database else { - fatalError("No database to save") + Logger.fatalError("No database to save") } let fileData = try targetDatabase.save() try fileData.write(to: kdbxAutofillURL, options: .atomic) } catch { - Diag.error("Failed to write autofill KDBX file [message: \(error.localizedDescription)]") + Logger.mainLog.error("Failed to write autofill KDBX file [message: \(error.localizedDescription, privacy: .public)]") } } } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/base32/Base32.swift b/ios/KdbxSwift/Sources/KdbxSwift/base32/Base32.swift index da05451..4aae4ff 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/base32/Base32.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/base32/Base32.swift @@ -25,6 +25,7 @@ // THE SOFTWARE. import Foundation +import os.log // https://tools.ietf.org/html/rfc4648 @@ -232,7 +233,7 @@ private func base32encode(_ data: UnsafeRawPointer, _ length: Int, _ table: [Int return base32Encoded } else { resultBuffer.deallocate() - fatalError("internal error") + Logger.fatalError("internal error") } } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/crypto/CryptoManager.swift b/ios/KdbxSwift/Sources/KdbxSwift/crypto/CryptoManager.swift index e295195..e18dd45 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/crypto/CryptoManager.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/crypto/CryptoManager.swift @@ -1,5 +1,6 @@ import Foundation import CommonCrypto.CommonHMAC +import os.log public enum CryptoError: Error { case invalidKDFParam(kdfName: String, paramName: String) @@ -36,7 +37,7 @@ public final class CryptoManager { return SecRandomCopyBytes(kSecRandomDefault, outBytes.count, &outBytes) } if status != errSecSuccess { - Diag.warning("Failed to generate random bytes [count: \(count), status: \(status)]") + Logger.mainLog.warning("Failed to generate random bytes [count: \(count, privacy: .public), status: \(status, privacy: .public)]") throw CryptoError.rngError(code: Int(status)) } return output diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/Database.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/Database.swift index e5332a1..9e8f535 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/Database.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/Database.swift @@ -1,3 +1,6 @@ +import Foundation +import os.log + public struct SearchQuery { public let includeSubgroups: Bool public let includeDeleted: Bool @@ -40,7 +43,7 @@ open class Database: Eraseable { } public var keyHelper: KeyHelper { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } internal init() { @@ -57,7 +60,7 @@ open class Database: Eraseable { } public class func isSignatureMatches(data: ByteArray) -> Bool { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func load( @@ -65,19 +68,19 @@ open class Database: Eraseable { dbFileData: ByteArray, preTransformedKeyMaterial: ByteArray ) throws { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func save() throws -> ByteArray { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func changeCompositeKey(to newKey: CompositeKey) { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func getBackupGroup(createIfMissing: Bool) -> Group? { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func count(includeGroups: Bool = true, includeEntries: Bool = true) -> Int { @@ -99,22 +102,22 @@ open class Database: Eraseable { } public func delete(group: Group) { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func delete(entry: Entry) { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func makeAttachment(name: String, data: ByteArray) -> Attachment { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } internal func resolveReferences( allEntries: T) where T: Collection, T.Element: Entry { - Diag.debug("Resolving references") + Logger.mainLog.debug("Resolving references") allEntries.forEach { entry in entry.fields.forEach { field in @@ -127,6 +130,6 @@ open class Database: Eraseable { field.resolveReferences(entries: allEntries) } } - Diag.debug("References resolved OK") + Logger.mainLog.debug("References resolved OK") } } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/DatabaseItem.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/DatabaseItem.swift index 79d220f..8173222 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/DatabaseItem.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/DatabaseItem.swift @@ -1,3 +1,5 @@ +import os.log + open class DatabaseItem { public enum TouchMode { case accessed @@ -18,6 +20,6 @@ open class DatabaseItem { } public func touch(_ mode: TouchMode, updateParents: Bool = true) { - fatalError("Pure abstract method") + Logger.fatalError("Pure abstract method") } } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/Entry.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/Entry.swift index 98fc9ec..7941e83 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/Entry.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/Entry.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class EntryField: Eraseable { public static let title = "Title" @@ -203,7 +204,7 @@ public class Entry: DatabaseItem, Eraseable { public var isHiddenFromSearch: Bool { get { return false } - set { fatalError("This property can be modified only in some DB formats") } + set { Logger.fatalError("This property can be modified only in some DB formats") } } public var attachments: Array @@ -298,7 +299,7 @@ public class Entry: DatabaseItem, Eraseable { } public func clone(makeNewUUID: Bool) -> Entry { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func apply(to target: Entry, makeNewUUID: Bool) { @@ -326,7 +327,7 @@ public class Entry: DatabaseItem, Eraseable { } public func backupState() { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } override public func touch(_ mode: DatabaseItem.TouchMode, updateParents: Bool = true) { diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/EntryFieldReference.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/EntryFieldReference.swift index ae2e1af..ca04af8 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/EntryFieldReference.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/EntryFieldReference.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class EntryFieldReference { public enum Status { @@ -81,7 +82,7 @@ public class EntryFieldReference { where T: Collection, T.Element: Entry { guard maxDepth > 0 else { - Diag.warning("Too many chained references") + Logger.mainLog.warning("Too many chained references") return .tooDeepReferences } @@ -132,20 +133,20 @@ public class EntryFieldReference { guard let targetFieldCode = string[targetFieldCodeRange].first, let targetFieldType = FieldType.fromCode(targetFieldCode) else { - Diag.debug("Unrecognized target field") + Logger.mainLog.debug("Unrecognized target field") continue } guard let searchFieldCode = string[searchFieldCodeRange].first, let searchFieldType = FieldType.fromCode(searchFieldCode) else { - Diag.debug("Unrecognized search field") + Logger.mainLog.debug("Unrecognized search field") continue } let searchValue = string[searchValueRange] guard !searchValue.isEmpty else { - Diag.debug("Empty search criterion") + Logger.mainLog.debug("Empty search criterion") continue } let ref = EntryFieldReference( @@ -225,7 +226,7 @@ public class EntryFieldReference { _uuid = UUID(uuidString: String(value)) } guard let uuid = _uuid else { - Diag.debug("Malformed UUID: \(value)") + Logger.mainLog.debug("Malformed UUID: \(value)") return nil } result = entries.first(where: { $0.uuid == uuid }) @@ -260,7 +261,7 @@ extension EntryFieldReference { case EntryField.notes: fieldCode = "N" default: - Diag.warning("References to custom fields are not supported") + Logger.mainLog.warning("References to custom fields are not supported") return nil } let result = "{REF:\(fieldCode)@I:\(entry.uuid.uuidString)}" diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/Group.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/Group.swift index 0ff44dc..ebe4ae6 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/Group.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/Group.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class Group: DatabaseItem, Eraseable { public static let defaultIconID = IconID.folder @@ -79,7 +80,7 @@ public class Group: DatabaseItem, Eraseable { } public func clone(makeNewUUID: Bool) -> Group { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func deepClone(makeNewUUIDs: Bool) -> Group { @@ -192,11 +193,11 @@ public class Group: DatabaseItem, Eraseable { } public func createEntry(detached: Bool = false) -> Entry { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func createGroup(detached: Bool = false) -> Group { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } override public func touch(_ mode: DatabaseItem.TouchMode, updateParents: Bool = true) { diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/KeyHelper.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/KeyHelper.swift index 5a8a565..b13dd7d 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/KeyHelper.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/KeyHelper.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class KeyHelper { public static let compositeKeyLength = 32 @@ -8,15 +9,15 @@ public class KeyHelper { passwordData: ByteArray, keyFileData: ByteArray ) throws -> ByteArray { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func getKey(fromCombinedComponents combinedComponents: ByteArray) -> ByteArray { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func getPasswordData(password: String) -> ByteArray { - fatalError("Pure virtual method") + Logger.fatalError("Pure virtual method") } public func processKeyFile(keyFileData: ByteArray) throws -> ByteArray { @@ -24,16 +25,16 @@ public class KeyHelper { let keyFileDataSize = keyFileData.count if keyFileDataSize == keyFileKeyLength { - Diag.debug("Key file format is: binary") + Logger.mainLog.debug("Key file format is: binary") return keyFileData } if let key = try processXmlKeyFile(keyFileData: keyFileData) { - Diag.debug("Key file format is: XML") + Logger.mainLog.debug("Key file format is: XML") return key } - Diag.debug("Key file format is: other") + Logger.mainLog.debug("Key file format is: other") return keyFileData.sha256 } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/DataCipher.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/DataCipher.swift index 7d799da..33c14e8 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/DataCipher.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/DataCipher.swift @@ -1,4 +1,5 @@ import Foundation +import os.log protocol DataCipher: AnyObject { var uuid: UUID { get } @@ -48,7 +49,7 @@ extension DataCipher { if keySize < hashSize { return hash.prefix(keySize) } else { - fatalError("Not implemented") + Logger.fatalError("Not implemented") } } } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/DataCipherFactory.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/DataCipherFactory.swift index 52a2754..7a5c154 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/DataCipherFactory.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/DataCipherFactory.swift @@ -1,4 +1,5 @@ import Foundation +import os.log final class DataCipherFactory { public static let instance = DataCipherFactory() @@ -12,13 +13,13 @@ final class DataCipherFactory { public func createFor(uuid: UUID) -> DataCipher? { switch uuid { case aes.uuid: - Diag.info("Creating AES cipher") + Logger.mainLog.info("Creating AES cipher") return AESDataCipher() case chacha20.uuid: - Diag.info("Creating ChaCha20 cipher") + Logger.mainLog.info("Creating ChaCha20 cipher") return ChaCha20DataCipher() default: - Diag.warning("Unrecognized cipher UUID") + Logger.mainLog.warning("Unrecognized cipher UUID") return nil } } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/StreamCipherFactory.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/StreamCipherFactory.swift index 164c05e..bc62428 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/StreamCipherFactory.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/cipher/StreamCipherFactory.swift @@ -1,4 +1,5 @@ import Foundation +import os.log internal enum ProtectedStreamAlgorithm: UInt32 { case Null = 0 @@ -26,10 +27,10 @@ final class StreamCipherFactory { static func create(algorithm: ProtectedStreamAlgorithm, key: ByteArray) -> StreamCipher { switch algorithm { case .Null: - Diag.verbose("Creating Null stream cipher") + Logger.mainLog.trace("Creating Null stream cipher") return UselessStreamCipher() case .ChaCha20: - Diag.verbose("Creating ChaCha20 stream cipher") + Logger.mainLog.trace("Creating ChaCha20 stream cipher") let sha512 = key.sha512 let chacha20 = sha512.withThrowableBytes { (sha512bytes) -> ChaCha20 in let chachaKey = ByteArray(bytes: sha512bytes.prefix(32)) diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kdf/Argon2KDF.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kdf/Argon2KDF.swift index 49c85b5..e7711a9 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kdf/Argon2KDF.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kdf/Argon2KDF.swift @@ -1,4 +1,5 @@ import Foundation +import os.log class AbstractArgon2KDF { public static let saltParam = "S" @@ -25,10 +26,10 @@ class AbstractArgon2KDF { fileprivate let defaultParallelism: UInt32 = 2 fileprivate var name: String { - fatalError("Abstract method, override this") + Logger.fatalError("Abstract method, override this") } fileprivate var uuid: UUID { - fatalError("Abstract method, override this") + Logger.fatalError("Abstract method, override this") } fileprivate var progress = ProgressEx() @@ -111,7 +112,7 @@ class AbstractArgon2KDF { } func transform(key: ByteArray, params: KDFParams) -> ByteArray { - fatalError("argon2 not implemented") + Logger.fatalError("argon2 not implemented") } } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kdf/KDFParams.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kdf/KDFParams.swift index d8b400b..485754b 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kdf/KDFParams.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kdf/KDFParams.swift @@ -1,4 +1,5 @@ import Foundation +import os.log final class KDFParams: VarDict { public static let uuidParam = "$UUID" @@ -12,16 +13,16 @@ final class KDFParams: VarDict { } override func read(data: ByteArray) -> Bool { - Diag.debug("Parsing KDF params") + Logger.mainLog.debug("Parsing KDF params") guard super.read(data: data) else { return false } guard let value = getValue(key: KDFParams.uuidParam) else { - Diag.warning("KDF UUID is missing") + Logger.mainLog.warning("KDF UUID is missing") return false } guard let uuidData = value.asByteArray(), let _ = UUID(data: uuidData) else { - Diag.warning("KDF UUID is malformed") + Logger.mainLog.warning("KDF UUID is malformed") return false } return true diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Attachment2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Attachment2.swift index 4210724..b435447 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Attachment2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Attachment2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class Attachment2: Attachment { public var id: Int @@ -28,7 +29,7 @@ public class Attachment2: Attachment { { assert(xml.name == Xml2.binary) - Diag.verbose("Loading XML: entry attachment") + Logger.mainLog.trace("Loading XML: entry attachment") var name: String? var binary: Binary2? for tag in xml.children { @@ -38,7 +39,7 @@ public class Attachment2: Attachment { case Xml2.value: let refString = tag.attributes[Xml2.ref] guard let binaryID = Int(refString) else { - Diag.error("Cannot parse Entry/Binary/Value/Ref as Int") + Logger.mainLog.error("Cannot parse Entry/Binary/Value/Ref as Int") throw Xml2.ParsingError.malformedValue( tag: "Entry/Binary/Value/Ref", value: refString) @@ -55,16 +56,16 @@ public class Attachment2: Attachment { ) } default: - Diag.error("Unexpected XML tag in Entry/Binary: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Entry/Binary: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "Entry/Binary/*") } } let _name = name ?? "" if _name.isEmpty { - Diag.error("Missing Entry/Binary/Name, ignoring") + Logger.mainLog.error("Missing Entry/Binary/Name, ignoring") } guard let _binary = binary else { - Diag.error("Missing Entry/Binary/Value") + Logger.mainLog.error("Missing Entry/Binary/Value") throw Xml2.ParsingError.malformedValue(tag: "Entry/Binary/Value/Ref", value: nil) } return Attachment2( @@ -75,7 +76,7 @@ public class Attachment2: Attachment { } internal func toXml() -> AEXMLElement { - Diag.verbose("Generating XML: entry attachment") + Logger.mainLog.trace("Generating XML: entry attachment") let xmlAtt = AEXMLElement(name: Xml2.binary) xmlAtt.addChild(name: Xml2.key, value: self.name) xmlAtt.addChild(name: Xml2.value, value: nil, attributes: [Xml2.ref: String(self.id)]) diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Binary2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Binary2.swift index fa75a81..21e65f7 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Binary2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Binary2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class Binary2: Eraseable { public typealias ID = Int @@ -35,11 +36,11 @@ public class Binary2: Eraseable { static func load(xml: AEXMLElement, streamCipher: StreamCipher) throws -> Binary2 { assert(xml.name == Xml2.binary) - Diag.verbose("Loading XML: binary") + Logger.mainLog.trace("Loading XML: binary") let idString = xml.attributes[Xml2.id] guard let id = Int(idString) else { - Diag.error("Cannot parse Meta/Binary/ID as Int") + Logger.mainLog.error("Cannot parse Meta/Binary/ID as Int") throw Xml2.ParsingError.malformedValue(tag: "Meta/Binary/ID", value: idString) } let isCompressedString = xml.attributes[Xml2.compressed] @@ -48,12 +49,12 @@ public class Binary2: Eraseable { let isProtected: Bool = Bool(string: isProtectedString ?? "") let base64 = xml.value ?? "" guard var data = ByteArray(base64Encoded: base64) else { - Diag.error("Cannot parse Meta/Binary/Value as Base64 string") + Logger.mainLog.error("Cannot parse Meta/Binary/Value as Base64 string") throw Xml2.ParsingError.malformedValue(tag: "Meta/Binary/ValueBase64", value: String(base64.prefix(16))) } if isProtected { - Diag.verbose("Decrypting binary") + Logger.mainLog.trace("Decrypting binary") data = try streamCipher.decrypt(data: data, progress: nil) } @@ -61,7 +62,7 @@ public class Binary2: Eraseable { } func toXml(streamCipher: StreamCipher) throws -> AEXMLElement { - Diag.verbose("Generating XML: binary") + Logger.mainLog.trace("Generating XML: binary") var attributes = [ Xml2.id: String(id), Xml2.compressed: isCompressed ? Xml2._true : Xml2._false @@ -69,7 +70,7 @@ public class Binary2: Eraseable { let value: ByteArray if isProtected { - Diag.verbose("Encrypting binary") + Logger.mainLog.trace("Encrypting binary") value = try streamCipher.encrypt(data: data, progress: nil) attributes[Xml2.protected] = Xml2._true } else { diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/CustomData2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/CustomData2.swift index 6be4071..033b9bc 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/CustomData2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/CustomData2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class CustomData2: Eraseable { @@ -57,7 +58,7 @@ public class CustomData2: Eraseable { xmlParentName: String ) throws { assert(xml.name == Xml2.customData) - Diag.verbose("Loading XML: custom data") + Logger.mainLog.trace("Loading XML: custom data") erase() for tag in xml.children { switch tag.name { @@ -68,9 +69,9 @@ public class CustomData2: Eraseable { timeParser: timeParser, xmlParentName: xmlParentName ) - Diag.verbose("Item loaded OK") + Logger.mainLog.trace("Item loaded OK") default: - Diag.error("Unexpected XML tag in CustomData: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in CustomData: \(tag.name)") throw Xml2.ParsingError.unexpectedTag( actual: tag.name, expected: xmlParentName + "/CustomData/*") @@ -97,20 +98,20 @@ public class CustomData2: Eraseable { case Xml2.lastModificationTime: optionalTimestamp = timeParser.xmlStringToDate(tag.value) default: - Diag.error("Unexpected XML tag in CustomData/Item: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in CustomData/Item: \(tag.name)") throw Xml2.ParsingError.unexpectedTag( actual: tag.name, expected: xmlParentName + "/CustomData/Item/*") } } guard let _key = key else { - Diag.error("Missing \(xmlParentName)/CustomData/Item/Key") + Logger.mainLog.error("Missing \(xmlParentName)/CustomData/Item/Key") throw Xml2.ParsingError.malformedValue( tag: xmlParentName + "/CustomData/Item/Key", value: nil) } guard let _value = value else { - Diag.error("Missing \(xmlParentName)/CustomData/Item/Value") + Logger.mainLog.error("Missing \(xmlParentName)/CustomData/Item/Value") throw Xml2.ParsingError.malformedValue( tag: xmlParentName + "/CustomData/Item/Value", value: nil) @@ -120,7 +121,7 @@ public class CustomData2: Eraseable { func toXml(timeFormatter: Database2XMLTimeFormatter) -> AEXMLElement { - Diag.verbose("Generating XML: custom data") + Logger.mainLog.trace("Generating XML: custom data") let xml = AEXMLElement(name: Xml2.customData) if dict.isEmpty { return xml diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/CustomIcon2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/CustomIcon2.swift index 7841fcb..4fcbd37 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/CustomIcon2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/CustomIcon2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class CustomIcon2: Eraseable { public private(set) var uuid: UUID @@ -54,7 +55,7 @@ public class CustomIcon2: Eraseable { func load(xml: AEXMLElement, timeParser: Database2XMLTimeParser) throws { assert(xml.name == Xml2.icon) - Diag.verbose("Loading XML: custom icon") + Logger.mainLog.trace("Loading XML: custom icon") erase() var xmlUUID: UUID? @@ -72,16 +73,16 @@ public class CustomIcon2: Eraseable { case Xml2.lastModificationTime: xmlLastModificationTime = timeParser.xmlStringToDate(tag.value) default: - Diag.error("Unexpected XML tag in CustomIcon: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in CustomIcon: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "CustomIcon/*") } } if xmlUUID == nil { - Diag.warning("Missing CustomIcon/UUID. Will generate a new one.") + Logger.mainLog.warning("Missing CustomIcon/UUID. Will generate a new one.") } let _uuid = xmlUUID ?? UUID() guard let _data = xmlData else { - Diag.error("Missing CustomIcon/Data") + Logger.mainLog.error("Missing CustomIcon/Data") throw Xml2.ParsingError.malformedValue(tag: "CustomIcon/Data", value: nil) } self.uuid = _uuid @@ -94,7 +95,7 @@ public class CustomIcon2: Eraseable { formatVersion: Database2.FormatVersion, timeFormatter: Database2XMLTimeFormatter ) -> AEXMLElement { - Diag.verbose("Generating XML: custom icon") + Logger.mainLog.trace("Generating XML: custom icon") let xmlIcon = AEXMLElement(name: Xml2.icon) xmlIcon.addChild(name: Xml2.uuid, value: uuid.base64EncodedString()) xmlIcon.addChild(name: Xml2.data, value: data.base64EncodedString()) diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Database2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Database2.swift index f36278d..2b34376 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Database2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Database2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log protocol Database2XMLTimeFormatter { func dateToXMLString(_ date: Date) -> String @@ -101,6 +102,7 @@ public class Database2: Database { private let _keyHelper = KeyHelper2() override public init() { + Logger.mainLog.debug("Database2 init") super.init() header = Header2(database: self) meta = Meta2(database: self) @@ -118,7 +120,7 @@ public class Database2: Database { hmacKey.erase() deletedObjects.removeAll() super.erase() - Diag.debug("DB memory cleaned up") + Logger.mainLog.debug("DB memory cleaned up") } internal static func makeNewV4() -> Database2 { @@ -151,10 +153,10 @@ public class Database2: Database { dbFileData: ByteArray, preTransformedKeyMaterial: ByteArray ) throws { - Diag.info("Loading KDBX database") + Logger.mainLog.info("Loading KDBX database") do { try header.read(data: dbFileData) - Diag.debug("Header read OK [format: \(header.formatVersion)]") + Logger.mainLog.debug("Header read OK [format: \(self.header.formatVersion, privacy: .public)]") importMasterKey(preTransformedKeyMaterial: preTransformedKeyMaterial, cipher: header.dataCipher) var decryptedData: ByteArray let dbWithoutHeader: ByteArray = dbFileData.suffix(from: header.size) @@ -162,13 +164,13 @@ public class Database2: Database { decryptedData = try decryptBlocksV4( data: dbWithoutHeader, cipher: header.dataCipher) - Diag.debug("Block decryption OK") + Logger.mainLog.debug("Block decryption OK") if header.isCompressed { - Diag.debug("Inflating Gzip data") + Logger.mainLog.debug("Inflating Gzip data") decryptedData = try decryptedData.gunzipped() } else { - Diag.debug("Data not compressed") + Logger.mainLog.debug("Data not compressed") } var xmlData: ByteArray @@ -176,7 +178,7 @@ public class Database2: Database { case .v4, .v4_1: let innerHeaderSize = try header.readInner(data: decryptedData) xmlData = decryptedData.suffix(from: innerHeaderSize) - Diag.debug("Inner header read OK") + Logger.mainLog.debug("Inner header read OK") } try load(xmlData: xmlData, timeParser: self) @@ -201,7 +203,7 @@ public class Database2: Database { allEntries: allEntriesPlusHistory ) - Diag.debug("Content loaded OK") + Logger.mainLog.debug("Content loaded OK") } catch is Header2.HeaderError { throw DatabaseError.loadError( reason: "load error" @@ -221,7 +223,7 @@ public class Database2: Database { } func decryptBlocksV4(data: ByteArray, cipher: DataCipher) throws -> ByteArray { - Diag.debug("Decrypting V4 blocks") + Logger.mainLog.debug("Decrypting V4 blocks") let inStream = data.asInputStream() inStream.open() defer { inStream.close() } @@ -230,7 +232,7 @@ public class Database2: Database { throw FormatError.prematureDataEnd } guard header.hash == storedHash else { - Diag.error("Header hash mismatch. Database corrupted?") + Logger.mainLog.error("Header hash mismatch. Database corrupted?") throw Header2.HeaderError.hashMismatch } @@ -239,11 +241,11 @@ public class Database2: Database { throw FormatError.prematureDataEnd } guard headerHMAC == storedHMAC else { - Diag.error("Header HMAC mismatch. Invalid master key?") + Logger.mainLog.error("Header HMAC mismatch. Invalid master key?") throw DatabaseError.invalidKey } - Diag.verbose("Reading blocks") + Logger.mainLog.trace("Reading blocks") let blockBytesCount = data.count - storedHash.count - storedHMAC.count let allBlocksData = ByteArray(capacity: blockBytesCount) @@ -269,7 +271,7 @@ public class Database2: Database { let dataForHMAC = ByteArray.concat(blockIndex.data, blockSize.data, blockData) let blockHMAC = CryptoManager.hmacSHA256(data: dataForHMAC, key: blockKey) guard blockHMAC == storedBlockHMAC else { - Diag.error("Block HMAC mismatch") + Logger.mainLog.error("Block HMAC mismatch") throw FormatError.blockHMACMismatch(blockIndex: Int(blockIndex)) } @@ -279,7 +281,7 @@ public class Database2: Database { blockIndex += 1 } - Diag.verbose("Will decrypt \(allBlocksData.count) bytes") + Logger.mainLog.trace("Will decrypt \(allBlocksData.count) bytes") #if DEBUG print("hmacKey plain: \(hmacKey.asHexString)") @@ -294,7 +296,7 @@ public class Database2: Database { key: cipherKey, iv: header.initialVector ) - Diag.verbose("Decrypted \(decryptedData.count) bytes") + Logger.mainLog.trace("Decrypted \(decryptedData.count) bytes") return decryptedData } @@ -308,14 +310,14 @@ public class Database2: Database { parsingOptions.documentHeader.standalone = "yes" parsingOptions.parserSettings.shouldTrimWhitespace = false do { - Diag.debug("Parsing XML") + Logger.mainLog.debug("Parsing XML") let xmlDoc = try AEXMLDocument(xml: xmlData.asData, options: parsingOptions) if let xmlError = xmlDoc.error { - Diag.error("Cannot parse XML: \(xmlError.localizedDescription)") + Logger.mainLog.error("Cannot parse XML: \(xmlError.localizedDescription)") throw Xml2.ParsingError.xmlError(details: xmlError.localizedDescription) } guard xmlDoc.root.name == Xml2.keePassFile else { - Diag.error("Not a KeePass XML document [xmlRoot: \(xmlDoc.root.name)]") + Logger.mainLog.error("Not a KeePass XML document [xmlRoot: \(xmlDoc.root.name)]") throw Xml2.ParsingError.notKeePassDocument } @@ -333,32 +335,32 @@ public class Database2: Database { ) if meta.headerHash != nil && (header.hash != meta.headerHash!) { - Diag.error("kdbx3 meta meta hash mismatch") + Logger.mainLog.error("kdbx3 meta meta hash mismatch") throw Header2.HeaderError.hashMismatch } - Diag.verbose("Meta loaded OK") + Logger.mainLog.trace("Meta loaded OK") case Xml2.root: try loadRoot( xml: tag, root: rootGroup, timeParser: timeParser ) - Diag.verbose("XML root loaded OK") + Logger.mainLog.trace("XML root loaded OK") default: throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "KeePassFile/*") } } self.root = rootGroup - Diag.debug("XML content loaded OK") + Logger.mainLog.debug("XML content loaded OK") } catch let error as Header2.HeaderError { - Diag.error("Header error [reason: \(error.localizedDescription)]") + Logger.mainLog.error("Header error [reason: \(error.localizedDescription)]") throw FormatError.parsingError(reason: error.localizedDescription) } catch let error as Xml2.ParsingError { - Diag.error("XML parsing error [reason: \(error.localizedDescription)]") + Logger.mainLog.error("XML parsing error [reason: \(error.localizedDescription)]") throw FormatError.parsingError(reason: error.localizedDescription) } catch let error as AEXMLError { - Diag.error("Raw XML parsing error [reason: \(error.localizedDescription)]") + Logger.mainLog.error("Raw XML parsing error [reason: \(error.localizedDescription)]") throw FormatError.parsingError(reason: error.localizedDescription) } } @@ -369,7 +371,7 @@ public class Database2: Database { timeParser: Database2XMLTimeParser ) throws { assert(xml.name == Xml2.root) - Diag.debug("Loading XML root") + Logger.mainLog.debug("Loading XML root") for tag in xml.children { switch tag.name { case Xml2.group: @@ -412,7 +414,21 @@ public class Database2: Database { let secureMasterSeed = header.masterSeed.clone() let joinedKey = ByteArray.concat(secureMasterSeed, preTransformedKeyMaterial) self.cipherKey = cipher.resizeKey(key: joinedKey) - let one = ByteArray(bytes: [1]) + + var oneBytes = [UInt8]() + oneBytes.append(1) + let one = ByteArray(bytes: oneBytes) + // SOMEHOW, when Swift evaluates this array literal assignment for the 2nd time, it decides to use 0 instead of 1 for + // the value it initialises with. Thus, it is critical that the hacky workaround above remain in place and no + // seemingly innocuous change like below is allowed to be made, at least until a version of Swift >5.7 + // resolves the bug or some deeper workaround in the ByteArray initialiser is made possible. + // Theory: [1] is syntactic sugar for a let declaration of a new array and thus the optimiser determines that + // it can never be mutated. Therefore when we actually do mutate it as part of the empty() memory sanitation + // step at the end of the first run through the autofill extension, we end up modifying something that the + // compiler has reasonably determined will never change and thus can be re-used safely in future. Creating + // oneBytes as a var ensures this optimisation is not performed. + //let one = ByteArray(bytes: [1]) + self.hmacKey = ByteArray.concat(joinedKey, one).sha512 compositeKey.setFinalKeys(hmacKey, cipherKey) } @@ -421,7 +437,13 @@ public class Database2: Database { let secureMasterSeed = header.masterSeed.clone() let joinedKey = ByteArray.concat(secureMasterSeed, key.combinedStaticComponents!) self.cipherKey = cipher.resizeKey(key: joinedKey) - let one = ByteArray(bytes: [1]) + + var oneBytes = [UInt8]() + oneBytes.append(1) + let one = ByteArray(bytes: oneBytes) + // See importMasterKey before changing this function + //let one = ByteArray(bytes: [1]) + self.hmacKey = ByteArray.concat(joinedKey, one).sha512 compositeKey.setFinalKeys(hmacKey, cipherKey) } @@ -435,19 +457,19 @@ public class Database2: Database { override public func getBackupGroup(createIfMissing: Bool) -> Group? { assert(root != nil) if !meta.isRecycleBinEnabled { - Diag.verbose("RecycleBin disabled in Meta") + Logger.mainLog.trace("RecycleBin disabled in Meta") return nil } guard let root = root else { - Diag.warning("Tried to get RecycleBin group without the root one") + Logger.mainLog.warning("Tried to get RecycleBin group without the root one") assertionFailure() return nil } if meta.recycleBinGroupUUID != UUID.ZERO { if let backupGroup = root.findGroup(byUUID: meta.recycleBinGroupUUID) { - Diag.verbose("RecycleBin group found") + Logger.mainLog.trace("RecycleBin group found") return backupGroup } } @@ -458,15 +480,15 @@ public class Database2: Database { backupGroup.isDeleted = true backupGroup.isSearchingEnabled = false backupGroup.isAutoTypeEnabled = false - Diag.verbose("RecycleBin group created") + Logger.mainLog.trace("RecycleBin group created") return backupGroup } - Diag.verbose("RecycleBin group not found nor created.") + Logger.mainLog.trace("RecycleBin group not found nor created.") return nil } private func updateBinaries(root: Group2) { - Diag.verbose("Updating all binaries") + Logger.mainLog.trace("Updating all binaries") var allEntries = [Entry2]() as [Entry] root.collectAllEntries(to: &allEntries) @@ -532,19 +554,19 @@ public class Database2: Database { } override public func save() throws -> ByteArray { - Diag.info("Saving KDBX database") + Logger.mainLog.info("Saving KDBX database") assert(root != nil, "Load or create a DB before saving.") header.maybeUpdateFormatVersion() let formatVersion = header.formatVersion - Diag.debug("Format version: \(formatVersion)") + Logger.mainLog.debug("Format version: \(formatVersion)") do { try header.randomizeSeeds() - Diag.debug("Seeds randomized OK") + Logger.mainLog.debug("Seeds randomized OK") rederiveMasterKey( key: compositeKey, cipher: header.dataCipher) - Diag.debug("Key derivation OK") + Logger.mainLog.debug("Key derivation OK") } catch let error as CryptoError { throw DatabaseError.saveError(reason: error.localizedDescription) } catch is KeyFileError { @@ -552,7 +574,7 @@ public class Database2: Database { } updateBinaries(root: root! as! Group2) - Diag.verbose("Binaries updated OK") + Logger.mainLog.trace("Binaries updated OK") let outStream = ByteArray.makeOutputStream() outStream.open() @@ -563,10 +585,10 @@ public class Database2: Database { meta.headerHash = header.hash let xmlString = try self.toXml(timeFormatter: self).xml let xmlData = ByteArray(utf8String: xmlString) - Diag.debug("XML generation OK") + Logger.mainLog.debug("XML generation OK") try encryptBlocksV4(to: outStream, xmlData: xmlData) - Diag.debug("Content encryption OK") + Logger.mainLog.debug("Content encryption OK") var allEntries = [Entry]() root?.collectAllEntries(to: &allEntries) @@ -578,7 +600,7 @@ public class Database2: Database { } internal func encryptBlocksV4(to outStream: ByteArray.OutputStream, xmlData: ByteArray) throws { - Diag.debug("Encrypting kdbx4 blocks") + Logger.mainLog.debug("Encrypting kdbx4 blocks") outStream.write(data: header.hash) outStream.write(data: header.getHMAC(key: hmacKey)) @@ -588,32 +610,32 @@ public class Database2: Database { do { try header.writeInner(to: contentStream) - Diag.verbose("Header written OK") + Logger.mainLog.trace("Header written OK") contentStream.write(data: xmlData) - guard let contentData = contentStream.data else { fatalError() } + guard let contentData = contentStream.data else { Logger.fatalError("Failed to get data from contentStream") } var dataToEncrypt = contentData if header.isCompressed { dataToEncrypt = try contentData.gzipped() - Diag.verbose("Gzip compression OK") + Logger.mainLog.trace("Gzip compression OK") } else { - Diag.verbose("No compression required") + Logger.mainLog.trace("No compression required") } - Diag.verbose("Encrypting \(dataToEncrypt.count) bytes") + Logger.mainLog.trace("Encrypting \(dataToEncrypt.count) bytes") let encData = try header.dataCipher.encrypt( plainText: dataToEncrypt, key: cipherKey, iv: header.initialVector.clone()) - Diag.verbose("Encrypted \(encData.count) bytes") + Logger.mainLog.trace("Encrypted \(encData.count) bytes") try writeAsBlocksV4(to: outStream, data: encData) - Diag.verbose("Blocks written OK") + Logger.mainLog.trace("Blocks written OK") } catch let error as Header2.HeaderError { - Diag.error("Header error [message: \(error.localizedDescription)]") + Logger.mainLog.error("Header error [message: \(error.localizedDescription)]") throw DatabaseError.saveError(reason: error.localizedDescription) } catch let error as GzipError { - Diag.error("Gzip error [kind: \(error.kind), message: \(error.message)]") + Logger.mainLog.error("Gzip error [kind: \(String(describing: error.kind)), message: \(error.message)]") let errMsg = String.localizedStringWithFormat( NSLocalizedString( "[Database2/Saving/Error] Data compression error: %@", @@ -623,7 +645,7 @@ public class Database2: Database { error.localizedDescription) throw DatabaseError.saveError(reason: errMsg) } catch let error as CryptoError { - Diag.error("Crypto error [reason: \(error.localizedDescription)]") + Logger.mainLog.error("Crypto error [reason: \(error.localizedDescription)]") let errMsg = String.localizedStringWithFormat( NSLocalizedString( "[Database2/Saving/Error] Encryption error: %@", @@ -636,12 +658,12 @@ public class Database2: Database { } internal func writeAsBlocksV4(to blockStream: ByteArray.OutputStream, data: ByteArray) throws { - Diag.debug("Writing kdbx4 blocks") + Logger.mainLog.debug("Writing kdbx4 blocks") let defaultBlockSize = 1024 * 1024 var blockStart: Int = 0 var blockIndex: UInt64 = 0 - Diag.verbose("\(data.count) bytes to write") + Logger.mainLog.trace("\(data.count) bytes to write") while blockStart != data.count { let blockSize = min(defaultBlockSize, data.count - blockStart) let blockData = data[blockStart.. AEXMLDocument { - Diag.debug("Will generate XML") + Logger.mainLog.debug("Will generate XML") var options = AEXMLOptions() options.documentHeader.encoding = "utf-8" options.documentHeader.standalone = "yes" @@ -680,7 +702,7 @@ public class Database2: Database { timeFormatter: timeFormatter ) ) - Diag.verbose("XML generation: Meta OK") + Logger.mainLog.trace("XML generation: Meta OK") let xmlRoot = xmlMain.addChild(name: Xml2.root) let root2 = root! as! Group2 @@ -690,7 +712,7 @@ public class Database2: Database { timeFormatter: timeFormatter ) xmlRoot.addChild(rootXML) - Diag.verbose("XML generation: Root group OK") + Logger.mainLog.trace("XML generation: Root group OK") let xmlDeletedObjects = xmlRoot.addChild(name: Xml2.deletedObjects) for deletedObject in deletedObjects { @@ -719,9 +741,9 @@ public class Database2: Database { } override public func delete(group: Group) { - guard let group = group as? Group2 else { fatalError() } + guard let group = group as? Group2 else { Logger.fatalError("Cannot delete group: not a Group") } guard let parentGroup = group.parent else { - Diag.warning("Cannot delete group: no parent group") + Logger.mainLog.warning("Cannot delete group: no parent group") return } @@ -731,7 +753,7 @@ public class Database2: Database { let moveOnly = !group.isDeleted && meta.isRecycleBinEnabled if moveOnly, let backupGroup = getBackupGroup(createIfMissing: meta.isRecycleBinEnabled) { - Diag.debug("Moving group to RecycleBin") + Logger.mainLog.debug("Moving group to RecycleBin") group.move(to: backupGroup) group.touch(.accessed, updateParents: false) @@ -739,7 +761,7 @@ public class Database2: Database { subGroups.forEach { $0.isDeleted = true } subEntries.forEach { $0.isDeleted = true } } else { - Diag.debug("Removing the group permanently.") + Logger.mainLog.debug("Removing the group permanently.") if group === getBackupGroup(createIfMissing: false) { meta?.resetRecycleBinGroupUUID() } @@ -748,17 +770,17 @@ public class Database2: Database { subEntries.forEach { addDeletedObject(uuid: $0.uuid) } parentGroup.remove(group: group) } - Diag.debug("Delete group OK") + Logger.mainLog.debug("Delete group OK") } override public func delete(entry: Entry) { guard let parentGroup = entry.parent else { - Diag.warning("Cannot delete entry: no parent group") + Logger.mainLog.warning("Cannot delete entry: no parent group") return } if entry.isDeleted { - Diag.debug("Already in Backup, removing permanently") + Logger.mainLog.debug("Already in Backup, removing permanently") addDeletedObject(uuid: entry.uuid) parentGroup.remove(entry: entry) return @@ -770,11 +792,11 @@ public class Database2: Database { entry.move(to: backupGroup) entry.touch(.accessed) } else { - Diag.debug("Backup disabled, removing permanently.") + Logger.mainLog.debug("Backup disabled, removing permanently.") addDeletedObject(uuid: entry.uuid) parentGroup.remove(entry: entry) } - Diag.debug("Delete entry OK") + Logger.mainLog.debug("Delete entry OK") } override public func makeAttachment(name: String, data: ByteArray) -> Attachment { @@ -785,7 +807,7 @@ public class Database2: Database { let compressedData = try data.gzipped() return Attachment2(name: name, isCompressed: true, data: compressedData) } catch { - Diag.warning("Failed to compress attachment data [message: \(error.localizedDescription)]") + Logger.mainLog.warning("Failed to compress attachment data [message: \(error.localizedDescription)]") } } @@ -800,7 +822,7 @@ public class Database2: Database { let newCustomIcon = CustomIcon2(uuid: UUID(), data: pngData) meta.addCustomIcon(newCustomIcon) - Diag.debug("Custom icon added OK") + Logger.mainLog.debug("Custom icon added OK") return newCustomIcon } @@ -815,13 +837,13 @@ public class Database2: Database { @discardableResult public func deleteCustomIcon(uuid: UUID) -> Bool { guard customIcons.contains(where: { $0.uuid == uuid }) else { - Diag.warning("Tried to delete non-existent custom icon") + Logger.mainLog.warning("Tried to delete non-existent custom icon") return false } meta.deleteCustomIcon(uuid: uuid) deletedObjects.append(DeletedObject2(database: self, uuid: uuid)) removeUnusedCustomIconRefs() - Diag.debug("Custom icon deleted OK") + Logger.mainLog.debug("Custom icon deleted OK") return true } @@ -846,7 +868,7 @@ extension Database2: Database2XMLTimeParser { return formatAppropriateDate } if let altFormatDate = Date(iso8601string: trimmedString) { - Diag.warning("Found ISO8601-formatted timestamp in \(header.formatVersion) DB.") + Logger.mainLog.warning("Found ISO8601-formatted timestamp in \(self.header.formatVersion) DB.") return altFormatDate } return nil diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/DeletedObject2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/DeletedObject2.swift index ab69507..8e19dbe 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/DeletedObject2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/DeletedObject2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class DeletedObject2: Eraseable { private weak var database: Database2? @@ -24,7 +25,7 @@ public class DeletedObject2: Eraseable { func load(xml: AEXMLElement, timeParser: Database2XMLTimeParser) throws { assert(xml.name == Xml2.deletedObject) - Diag.verbose("Loading XML: deleted object") + Logger.mainLog.trace("Loading XML: deleted object") erase() for tag in xml.children { switch tag.name { @@ -32,14 +33,14 @@ public class DeletedObject2: Eraseable { self.uuid = UUID(base64Encoded: tag.value) ?? UUID.ZERO case Xml2.deletionTime: guard let deletionTime = timeParser.xmlStringToDate(tag.value) else { - Diag.error("Cannot parse DeletedObject/DeletionTime as Date") + Logger.mainLog.error("Cannot parse DeletedObject/DeletionTime as Date") throw Xml2.ParsingError.malformedValue( tag: "DeletedObject/DeletionTime", value: tag.value) } self.deletionTime = deletionTime default: - Diag.error("Unexpected XML tag in DeletedObject: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in DeletedObject: \(tag.name)") throw Xml2.ParsingError.unexpectedTag( actual: tag.name, expected: "DeletedObject/*") @@ -48,7 +49,7 @@ public class DeletedObject2: Eraseable { } func toXml(timeFormatter: Database2XMLTimeFormatter) -> AEXMLElement { - Diag.verbose("Generating XML: deleted object") + Logger.mainLog.trace("Generating XML: deleted object") let xml = AEXMLElement(name: Xml2.deletedObject) xml.addChild(name: Xml2.uuid, value: uuid.base64EncodedString()) xml.addChild(name: Xml2.deletionTime, value: timeFormatter.dateToXMLString(deletionTime)) diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Entry2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Entry2.swift index 545e640..4b09d49 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Entry2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Entry2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class EntryField2: EntryField { @@ -37,7 +38,7 @@ public class EntryField2: EntryField { func load(xml: AEXMLElement, streamCipher: StreamCipher) throws { assert(xml.name == Xml2.string) - Diag.verbose("Loading XML: entry field") + Logger.mainLog.trace("Loading XML: entry field") erase() @@ -52,35 +53,31 @@ public class EntryField2: EntryField { isProtected = Bool(string: tag.attributes[Xml2.protected]) if isProtected { if let encData = ByteArray(base64Encoded: tag.value ?? "") { - Diag.verbose("Decrypting field value") + Logger.mainLog.trace("Decrypting field value") let plainData = try streamCipher.decrypt(data: encData, progress: nil) value = plainData.toString(using: .utf8) if value == nil { - Diag.warning("Failed to decrypt field value") - if Diag.isDeepDebugMode() { - Diag.debug("Encrypted field value: `\(encData.asHexString)`") - Diag.debug("Decrypted field value: `\(plainData.asHexString)`") - } + Logger.mainLog.warning("Failed to decrypt field value") } } } else { value = tag.value ?? "" } default: - Diag.error("Unexpected XML tag in Entry/String: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Entry/String: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "Entry/String/*") } } guard let _key = key else { - Diag.error("Missing Entry/String/Key") + Logger.mainLog.error("Missing Entry/String/Key") throw Xml2.ParsingError.malformedValue(tag: "Entry/String/Key", value: nil) } guard let _value = value else { - Diag.error("Missing Entry/String/Value") + Logger.mainLog.error("Missing Entry/String/Value") throw Xml2.ParsingError.malformedValue(tag: "Entry/String/Value", value: nil) } if _key.isEmpty && _value.isNotEmpty { - Diag.error("Missing Entry/String/Key with present Value") + Logger.mainLog.error("Missing Entry/String/Key with present Value") } self.name = _key self.value = _value @@ -89,12 +86,12 @@ public class EntryField2: EntryField { func toXml(streamCipher: StreamCipher) throws -> AEXMLElement { - Diag.verbose("Generating XML: entry string") + Logger.mainLog.trace("Generating XML: entry string") let xmlField = AEXMLElement(name: Xml2.string) xmlField.addChild(name: Xml2.key, value: name) if isProtected { let openData = ByteArray(utf8String: value) - Diag.verbose("Encrypting field value") + Logger.mainLog.trace("Encrypting field value") let encData = try streamCipher.encrypt(data: openData, progress: nil) xmlField.addChild( name: Xml2.value, @@ -148,7 +145,7 @@ public class Entry2: Entry { func load(xml: AEXMLElement, streamCipher: StreamCipher) throws { assert(xml.name == Xml2.autoType) - Diag.verbose("Loading XML: entry autotype") + Logger.mainLog.trace("Loading XML: entry autotype") erase() for tag in xml.children { @@ -162,7 +159,7 @@ public class Entry2: Entry { case Xml2.association: try loadAssociation(xml: tag) default: - Diag.error("Unexpected XML tag in Entry/AutoType: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Entry/AutoType: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "Entry/*") } } @@ -180,20 +177,20 @@ public class Entry2: Entry { case Xml2.keystrokeSequence: sequence = tag.value ?? "" default: - Diag.error("Unexpected XML tag in Entry/AutoType/Association: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Entry/AutoType/Association: \(tag.name)") throw Xml2.ParsingError.unexpectedTag( actual: tag.name, expected: "Entry/AutoType/Association/*") } } guard window != nil else { - Diag.error("Missing Entry/AutoType/Association/Window") + Logger.mainLog.error("Missing Entry/AutoType/Association/Window") throw Xml2.ParsingError.malformedValue( tag: "Entry/AutoType/Association/Window", value: window) } guard sequence != nil else { - Diag.error("Missing Entry/AutoType/Association/Sequence") + Logger.mainLog.error("Missing Entry/AutoType/Association/Sequence") throw Xml2.ParsingError.malformedValue( tag: "Entry/AutoType/Association/Sequence", value: sequence) @@ -202,7 +199,7 @@ public class Entry2: Entry { } internal func toXml() -> AEXMLElement { - Diag.verbose("Generating XML: entry autotype") + Logger.mainLog.trace("Generating XML: entry autotype") let xmlAutoType = AEXMLElement(name: Xml2.autoType) xmlAutoType.addChild( name: Xml2.enabled, @@ -314,7 +311,7 @@ public class Entry2: Entry { override public func apply(to target: Entry, makeNewUUID: Bool) { super.apply(to: target, makeNewUUID: makeNewUUID) guard let targetEntry2 = target as? Entry2 else { - Diag.warning("Tried to apply entry state to unexpected entry class") + Logger.mainLog.warning("Tried to apply entry state to unexpected entry class") assertionFailure() return } @@ -397,7 +394,7 @@ public class Entry2: Entry { timeParser: Database2XMLTimeParser ) throws { assert(xml.name == Xml2.entry) - Diag.verbose("Loading XML: entry") + Logger.mainLog.trace("Loading XML: entry") let parent = self.parent erase() @@ -423,17 +420,17 @@ public class Entry2: Entry { let field = makeEntryField(name: "", value: "", isProtected: true) as! EntryField2 try field.load(xml: tag, streamCipher: streamCipher) if field.isEmpty { - Diag.debug("Loaded empty entry field, ignoring.") + Logger.mainLog.debug("Loaded empty entry field, ignoring.") } else if field.name.isEmpty { - Diag.warning("Loaded entry field with an empty name, will show a warning.") + Logger.mainLog.warning("Loaded entry field with an empty name, will show a warning.") setField(name: field.name, value: field.value, isProtected: field.isProtected) } else { - Diag.verbose("Entry field loaded OK") + Logger.mainLog.trace("Entry field loaded OK") setField(name: field.name, value: field.value, isProtected: field.isProtected) } case Xml2.binary: guard !tag.children.isEmpty else { - Diag.warning("Skipping an empty Binary tag") + Logger.mainLog.warning("Skipping an empty Binary tag") continue } let att = try Attachment2.load( @@ -441,13 +438,13 @@ public class Entry2: Entry { database: database as! Database2, streamCipher: streamCipher) attachments.append(att) - Diag.verbose("Entry attachment loaded OK") + Logger.mainLog.trace("Entry attachment loaded OK") case Xml2.times: try loadTimes(xml: tag, timeParser: timeParser) - Diag.verbose("Entry times loaded OK") + Logger.mainLog.trace("Entry times loaded OK") case Xml2.autoType: try autoType.load(xml: tag, streamCipher: streamCipher) - Diag.verbose("Entry autotype loaded OK") + Logger.mainLog.trace("Entry autotype loaded OK") case Xml2.previousParentGroup: assert(formatVersion >= .v4) previousParentGroupUUID = UUID(base64Encoded: tag.value) ?? UUID.ZERO @@ -461,7 +458,7 @@ public class Entry2: Entry { streamCipher: streamCipher, timeParser: timeParser, xmlParentName: "Entry") - Diag.verbose("Entry custom data loaded OK") + Logger.mainLog.trace("Entry custom data loaded OK") case Xml2.history: try loadHistory( xml: tag, @@ -469,9 +466,9 @@ public class Entry2: Entry { streamCipher: streamCipher, timeParser: timeParser ) - Diag.verbose("Entry history loaded OK") + Logger.mainLog.trace("Entry history loaded OK") default: - Diag.error("Unexpected XML tag in Entry: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Entry: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "Entry/*") } } @@ -479,14 +476,14 @@ public class Entry2: Entry { func loadTimes(xml: AEXMLElement, timeParser: Database2XMLTimeParser) throws { assert(xml.name == Xml2.times) - Diag.verbose("Loading XML: entry times") + Logger.mainLog.trace("Loading XML: entry times") var optionalExpiryTime: Date? for tag in xml.children { switch tag.name { case Xml2.lastModificationTime: guard let time = timeParser.xmlStringToDate(tag.value) else { - Diag.error("Cannot parse Entry/Times/LastModificationTime as Date") + Logger.mainLog.error("Cannot parse Entry/Times/LastModificationTime as Date") throw Xml2.ParsingError.malformedValue( tag: "Entry/Times/LastModificationTime", value: tag.value) @@ -494,7 +491,7 @@ public class Entry2: Entry { lastModificationTime = time case Xml2.creationTime: guard let time = timeParser.xmlStringToDate(tag.value) else { - Diag.error("Cannot parse Entry/Times/CreationTime as Date") + Logger.mainLog.error("Cannot parse Entry/Times/CreationTime as Date") throw Xml2.ParsingError.malformedValue( tag: "Entry/Times/CreationTime", value: tag.value) @@ -502,7 +499,7 @@ public class Entry2: Entry { creationTime = time case Xml2.lastAccessTime: guard let time = timeParser.xmlStringToDate(tag.value) else { - Diag.error("Cannot parse Entry/Times/LastAccessTime as Date") + Logger.mainLog.error("Cannot parse Entry/Times/LastAccessTime as Date") throw Xml2.ParsingError.malformedValue( tag: "Entry/Times/LastAccessTime", value: tag.value) @@ -510,12 +507,12 @@ public class Entry2: Entry { lastAccessTime = time case Xml2.expiryTime: guard let tagValue = tag.value else { - Diag.warning("Entry/Times/ExpiryTime is nil") + Logger.mainLog.warning("Entry/Times/ExpiryTime is nil") optionalExpiryTime = nil continue } guard let time = timeParser.xmlStringToDate(tagValue) else { - Diag.error("Cannot parse Entry/Times/ExpiryTime as Date") + Logger.mainLog.error("Cannot parse Entry/Times/ExpiryTime as Date") throw Xml2.ParsingError.malformedValue( tag: "Entry/Times/ExpiryTime", value: tagValue) @@ -527,14 +524,14 @@ public class Entry2: Entry { usageCount = UInt32(tag.value) ?? 0 case Xml2.locationChanged: guard let time = timeParser.xmlStringToDate(tag.value) else { - Diag.error("Cannot parse Entry/Times/LocationChanged as Date") + Logger.mainLog.error("Cannot parse Entry/Times/LocationChanged as Date") throw Xml2.ParsingError.malformedValue( tag: "Entry/Times/LocationChanged", value: tag.value) } locationChangedTime = time default: - Diag.error("Unexpected XML tag in Entry/Times: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Entry/Times: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "Entry/Times/*") } } @@ -543,7 +540,7 @@ public class Entry2: Entry { self.expiryTime = expiryTime } else { if canExpire { - Diag.error("Parsed an entry that can expire, but Entry/Times/ExpiryTime is nil") + Logger.mainLog.error("Parsed an entry that can expire, but Entry/Times/ExpiryTime is nil") throw Xml2.ParsingError.malformedValue( tag: "Entry/Times/ExpiryTime", value: nil) @@ -561,7 +558,7 @@ public class Entry2: Entry { ) throws { assert(xml.name == Xml2.history) - Diag.verbose("Loading XML: entry history") + Logger.mainLog.trace("Loading XML: entry history") for tag in xml.children { switch tag.name { case Xml2.entry: @@ -573,9 +570,9 @@ public class Entry2: Entry { timeParser: timeParser ) history.append(histEntry) - Diag.verbose("Entry history item loaded OK") + Logger.mainLog.trace("Entry history item loaded OK") default: - Diag.error("Unexpected XML tag in Entry/History: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Entry/History: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "Entry/History/*") } } @@ -586,7 +583,7 @@ public class Entry2: Entry { streamCipher: StreamCipher, timeFormatter: Database2XMLTimeFormatter ) throws -> AEXMLElement { - Diag.verbose("Generating XML: entry") + Logger.mainLog.trace("Generating XML: entry") let meta: Meta2 = (database as! Database2).meta let xmlEntry = AEXMLElement(name: Xml2.entry) diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Group2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Group2.swift index b04b849..6fab37e 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Group2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Group2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class Group2: Group { public var isExpanded: Bool @@ -65,7 +66,7 @@ public class Group2: Group { override public func apply(to target: Group, makeNewUUID: Bool) { super.apply(to: target, makeNewUUID: makeNewUUID) guard let targetGroup2 = target as? Group2 else { - Diag.warning("Tried to apply group state to unexpected group class") + Logger.mainLog.warning("Tried to apply group state to unexpected group class") assertionFailure() return } @@ -129,7 +130,7 @@ public class Group2: Group { timeParser: Database2XMLTimeParser ) throws { assert(xml.name == Xml2.group) - Diag.verbose("Loading XML: group") + Logger.mainLog.trace("Loading XML: group") let parent = self.parent erase() @@ -144,7 +145,7 @@ public class Group2: Group { case Xml2.uuid: self.uuid = UUID(base64Encoded: tag.value) ?? UUID.ZERO if uuid == meta.recycleBinGroupUUID && meta.isRecycleBinEnabled { - Diag.verbose("Is a backup group") + Logger.mainLog.trace("Is a backup group") isRecycleBin = true } case Xml2.name: @@ -161,7 +162,7 @@ public class Group2: Group { self.customIconUUID = UUID(base64Encoded: tag.value) ?? UUID.ZERO case Xml2.times: try loadTimes(xml: tag, timeParser: timeParser) - Diag.verbose("Group times loaded OK") + Logger.mainLog.trace("Group times loaded OK") case Xml2.isExpanded: self.isExpanded = Bool(string: tag.value) case Xml2.defaultAutoTypeSequence: @@ -186,7 +187,7 @@ public class Group2: Group { timeParser: timeParser, xmlParentName: "Group" ) - Diag.verbose("Custom data loaded OK") + Logger.mainLog.trace("Custom data loaded OK") case Xml2.group: let subGroup = Group2(database: database) try subGroup.load( @@ -196,7 +197,7 @@ public class Group2: Group { timeParser: timeParser ) self.add(group: subGroup) - Diag.verbose("Subgroup loaded OK") + Logger.mainLog.trace("Subgroup loaded OK") case Xml2.entry: let entry = Entry2(database: database) try entry.load( @@ -206,9 +207,9 @@ public class Group2: Group { timeParser: timeParser ) self.add(entry: entry) - Diag.verbose("Entry loaded OK") + Logger.mainLog.trace("Entry loaded OK") default: - Diag.error("Unexpected XML tag in Group: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Group: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "Group/*") } } @@ -224,11 +225,11 @@ public class Group2: Group { timeParser: Database2XMLTimeParser ) throws -> Date { if (value == nil || value!.isEmpty) && fallbackToEpoch { - Diag.warning("\(tag) is empty, will use 1970-01-01 instead") + Logger.mainLog.warning("\(tag) is empty, will use 1970-01-01 instead") return Date(timeIntervalSince1970: 0.0) } guard let time = timeParser.xmlStringToDate(value) else { - Diag.error("Cannot parse \(tag) as Date") + Logger.mainLog.error("Cannot parse \(tag) as Date") throw Xml2.ParsingError.malformedValue( tag: tag, value: value) @@ -238,7 +239,7 @@ public class Group2: Group { func loadTimes(xml: AEXMLElement, timeParser: Database2XMLTimeParser) throws { assert(xml.name == Xml2.times) - Diag.verbose("Loading XML: group times") + Logger.mainLog.trace("Loading XML: group times") for tag in xml.children { switch tag.name { @@ -277,7 +278,7 @@ public class Group2: Group { fallbackToEpoch: true, timeParser: timeParser) default: - Diag.error("Unexpected XML tag in Group/Times: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Group/Times: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "Group/Times/*") } } @@ -288,7 +289,7 @@ public class Group2: Group { streamCipher: StreamCipher, timeFormatter: Database2XMLTimeFormatter ) throws -> AEXMLElement { - Diag.verbose("Generating XML: group") + Logger.mainLog.trace("Generating XML: group") let xmlGroup = AEXMLElement(name: Xml2.group) xmlGroup.addChild(name: Xml2.uuid, value: uuid.base64EncodedString()) xmlGroup.addChild(name: Xml2.name, value: name) diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Header2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Header2.swift index 317e4d2..5d40c81 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Header2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Header2.swift @@ -1,5 +1,6 @@ import Foundation import CommonCrypto +import os.log final class Header2: Eraseable { private static let signature1: UInt32 = 0x9AA2D903 @@ -202,6 +203,7 @@ final class Header2: Eraseable { } init(database: Database2) { + Logger.mainLog.debug("DB header init") self.database = database initialized = false formatVersion = .v4 @@ -220,6 +222,7 @@ final class Header2: Eraseable { } func erase() { + Logger.mainLog.debug("DB header erase") initialized = false formatVersion = .v4 data.erase() @@ -271,23 +274,23 @@ final class Header2: Eraseable { guard let sign1: UInt32 = stream.readUInt32(), let sign2: UInt32 = stream.readUInt32() else { - Diag.error("Signature is too short") + Logger.mainLog.error("Signature is too short") throw HeaderError.readingError } headerSize += sign1.byteWidth + sign2.byteWidth guard sign1 == Header2.signature1 else { - Diag.error("Wrong signature #1") + Logger.mainLog.error("Wrong signature #1") throw HeaderError.wrongSignature } guard sign2 == Header2.signature2 else { - Diag.error("Wrong signature #2") + Logger.mainLog.error("Wrong signature #2") throw HeaderError.wrongSignature } } private func readFormatVersion(stream: ByteArray.InputStream, headerSize: inout Int) throws { guard let fileVersion: UInt32 = stream.readUInt32() else { - Diag.error("Signature is too short") + Logger.mainLog.error("Signature is too short") throw HeaderError.readingError } headerSize += fileVersion.byteWidth @@ -298,18 +301,20 @@ final class Header2: Eraseable { if fileVersion == Header2.fileVersion4_1 { formatVersion = .v4_1 } - Diag.verbose("Database format: \(formatVersion)") + Logger.mainLog.trace("Database format: \(self.formatVersion)") return } - Diag.error("Unsupported file version [version: \(fileVersion.asHexString)]") + Logger.mainLog.error("Unsupported file version [version: \(fileVersion.asHexString, privacy: .public)]") throw HeaderError.unsupportedFileVersion(actualVersion: fileVersion.asHexString) } func read(data inputData: ByteArray) throws { - assert(!initialized, "Tried to read already initialized header") + if (initialized) { + Logger.fatalError("Tried to read already initialized header") + } - Diag.verbose("Will read header") + Logger.mainLog.trace("Will read header") var headerSize = 0 let stream = inputData.asInputStream() stream.open() @@ -317,7 +322,7 @@ final class Header2: Eraseable { try verifyFileSignature(stream: stream, headerSize: &headerSize) try readFormatVersion(stream: stream, headerSize: &headerSize) - Diag.verbose("Header signatures OK") + Logger.mainLog.trace("Header signatures OK") while (true) { guard let rawFieldID: UInt8 = stream.readUInt8() else { throw HeaderError.readingError } @@ -329,7 +334,7 @@ final class Header2: Eraseable { headerSize += MemoryLayout.size(ofValue: fSize) + fieldSize guard let fieldID: FieldID = FieldID(rawValue: rawFieldID) else { - Diag.warning("Unknown field ID, skipping [fieldID: \(rawFieldID)]") + Logger.mainLog.warning("Unknown field ID, skipping [fieldID: \(rawFieldID)]") continue } @@ -345,70 +350,70 @@ final class Header2: Eraseable { switch fieldID { case .end: - Diag.verbose("\(fieldID.name) read OK") + Logger.mainLog.trace("\(fieldID.name) read OK") break case .comment: - Diag.verbose("\(fieldID.name) read OK") + Logger.mainLog.trace("\(fieldID.name) read OK") break case .cipherID: guard let _cipherUUID = UUID(data: fieldValueData) else { - Diag.error("Cipher UUID is misformatted") + Logger.mainLog.error("Cipher UUID is misformatted") throw HeaderError.corruptedField(fieldName: fieldID.name) } guard let _dataCipher = DataCipherFactory.instance.createFor(uuid: _cipherUUID) else { - Diag.error("Unsupported cipher ID: \(fieldValueData.asHexString)") + Logger.mainLog.error("Unsupported cipher ID: \(fieldValueData.asHexString, privacy: .public)") throw HeaderError.unsupportedDataCipher( uuidHexString: fieldValueData.asHexString) } self.dataCipher = _dataCipher - Diag.verbose("\(fieldID.name) read OK [name: \(dataCipher.name)]") + Logger.mainLog.trace("\(fieldID.name) read OK [name: \(self.dataCipher.name)]") case .compressionFlags: guard let compressionFlags32 = UInt32(data: fieldValueData) else { throw HeaderError.readingError } guard let compressionFlags8 = UInt8(exactly: compressionFlags32) else { - Diag.error("Unknown compression algorithm [compressionFlags32: \(compressionFlags32)]") + Logger.mainLog.error("Unknown compression algorithm [compressionFlags32: \(compressionFlags32, privacy: .public)]") throw HeaderError.unknownCompressionAlgorithm } guard CompressionAlgorithm(rawValue: compressionFlags8) != nil else { - Diag.error("Unknown compression algorithm [compressionFlags8: \(compressionFlags8)]") + Logger.mainLog.error("Unknown compression algorithm [compressionFlags8: \(compressionFlags8, privacy: .public)]") throw HeaderError.unknownCompressionAlgorithm } - Diag.verbose("\(fieldID.name) read OK") + Logger.mainLog.trace("\(fieldID.name) read OK") case .masterSeed: guard fieldSize == SHA256_SIZE else { - Diag.error("Unexpected \(fieldID.name) field size [\(fieldSize) bytes]") + Logger.mainLog.error("Unexpected \(fieldID.name) field size [\(fieldSize) bytes]") throw HeaderError.corruptedField(fieldName: fieldID.name) } - Diag.verbose("\(fieldID.name) read OK") + Logger.mainLog.trace("\(fieldID.name) read OK") case .encryptionIV: - Diag.verbose("\(fieldID.name) read OK") + Logger.mainLog.trace("\(fieldID.name) read OK") break case .kdfParameters: guard formatVersion >= .v4 else { - Diag.error("Found \(fieldID.name) in non-V4 header. Database corrupted?") + Logger.mainLog.error("Found \(fieldID.name) in non-V4 header. Database corrupted?") throw HeaderError.corruptedField(fieldName: fieldID.name) } guard let kdfParams = KDFParams(data: fieldValueData) else { - Diag.error("Cannot parse KDF params. Database corrupted?") + Logger.mainLog.error("Cannot parse KDF params. Database corrupted?") throw HeaderError.corruptedField(fieldName: fieldID.name) } self.kdfParams = kdfParams self.kdf = Argon2dKDF() - Diag.verbose("\(fieldID.name) read OK") + Logger.mainLog.trace("\(fieldID.name) read OK") case .publicCustomData: guard formatVersion >= .v4 else { - Diag.error("Found \(fieldID.name) in non-V4 header. Database corrupted?") + Logger.mainLog.error("Found \(fieldID.name) in non-V4 header. Database corrupted?") throw HeaderError.corruptedField(fieldName: fieldID.name) } guard let publicCustomData = VarDict(data: fieldValueData) else { - Diag.error("Cannot parse public custom data. Database corrupted?") + Logger.mainLog.error("Cannot parse public custom data. Database corrupted?") throw HeaderError.corruptedField(fieldName: fieldID.name) } self.publicCustomData = publicCustomData - Diag.verbose("\(fieldID.name) read OK") + Logger.mainLog.trace("\(fieldID.name) read OK") default: throw HeaderError.corruptedField(fieldName: fieldID.name) } @@ -419,36 +424,36 @@ final class Header2: Eraseable { self.hash = self.data.sha256 try verifyImportantFields() - Diag.verbose("All important fields are in place") + Logger.mainLog.trace("All important fields are in place") } private func verifyImportantFields() throws { - Diag.verbose("Will check all important fields are present") + Logger.mainLog.trace("Will check all important fields are present") var importantFields: [FieldID] importantFields = [.cipherID, .compressionFlags, .masterSeed, .encryptionIV, .kdfParameters] for fieldID in importantFields { guard let fieldData = fields[fieldID] else { - Diag.error("\(fieldID.name) is missing") + Logger.mainLog.error("\(fieldID.name, privacy: .public) is missing") throw HeaderError.corruptedField(fieldName: fieldID.name) } if fieldData.isEmpty { - Diag.error("\(fieldID.name) is present, but empty") + Logger.mainLog.error("\(fieldID.name, privacy: .public) is present, but empty") throw HeaderError.corruptedField(fieldName: fieldID.name) } } - Diag.verbose("All important fields are OK") + Logger.mainLog.trace("All important fields are OK") guard initialVector.count == dataCipher.initialVectorSize else { - Diag.error("Initial vector size is inappropritate for the cipher [size: \(initialVector.count), cipher UUID: \(dataCipher.uuid)]") + Logger.mainLog.error("Initial vector size is inappropritate for the cipher [size: \(self.initialVector.count, privacy: .public), cipher UUID: \(self.dataCipher.uuid, privacy: .public)]") throw HeaderError.corruptedField(fieldName: FieldID.encryptionIV.name) } } internal func initStreamCipher() { guard let protectedStreamKey = protectedStreamKey else { - fatalError() + Logger.fatalError("initStreamCipher: Failed to assign protectedStreamKey") } self.streamCipher = StreamCipherFactory.create( algorithm: innerStreamAlgorithm, @@ -470,7 +475,7 @@ final class Header2: Eraseable { stream.open() defer { stream.close() } - Diag.verbose("Will read inner header") + Logger.mainLog.trace("Will read inner header") var size: Int = 0 while true { guard let rawFieldID = stream.readUInt8() else { @@ -498,17 +503,17 @@ final class Header2: Eraseable { throw HeaderError.corruptedField(fieldName: fieldID.name) } guard let protectedStreamAlgorithm = ProtectedStreamAlgorithm(rawValue: rawID) else { - Diag.error("Unrecognized protected stream algorithm [rawID: \(rawID)]") + Logger.mainLog.error("Unrecognized protected stream algorithm [rawID: \(rawID, privacy: .public)]") throw HeaderError.unsupportedStreamCipher(id: rawID) } self.innerStreamAlgorithm = protectedStreamAlgorithm - Diag.verbose("\(fieldID.name) read OK [name: \(innerStreamAlgorithm.name)]") + Logger.mainLog.trace("\(fieldID.name) read OK [name: \(self.innerStreamAlgorithm.name, privacy: .public)]") case .innerRandomStreamKey: guard fieldData.count > 0 else { throw HeaderError.corruptedField(fieldName: fieldID.name) } self.protectedStreamKey = fieldData.clone() - Diag.verbose("\(fieldID.name) read OK") + Logger.mainLog.trace("\(fieldID.name) read OK") case .binary: let isProtected = (fieldData[0] & 0x01 != 0) let newBinaryID = database.binaries.count @@ -518,11 +523,11 @@ final class Header2: Eraseable { isCompressed: false, isProtected: isProtected) database.binaries[newBinaryID] = binary - Diag.verbose("\(fieldID.name) read OK [size: \(fieldData.count) bytes]") + Logger.mainLog.trace("\(fieldID.name, privacy: .public) read OK [size: \(fieldData.count) bytes]") case .end: initStreamCipher() - Diag.verbose("Stream cipher init OK") - Diag.verbose("Inner header read OK [size: \(size) bytes]") + Logger.mainLog.trace("Stream cipher init OK") + Logger.mainLog.trace("Inner header read OK [size: \(size) bytes]") return size } } @@ -532,7 +537,7 @@ final class Header2: Eraseable { } func write(to outStream: ByteArray.OutputStream) { - Diag.verbose("Will write header") + Logger.mainLog.trace("Will write header") let headerStream = ByteArray.makeOutputStream() headerStream.open() defer { headerStream.close() } @@ -543,11 +548,11 @@ final class Header2: Eraseable { case .v4: headerStream.write(value: Header2.fileVersion4) writeV4(stream: headerStream) - Diag.verbose("kdbx4 header written OK") + Logger.mainLog.trace("kdbx4 header written OK") case .v4_1: headerStream.write(value: Header2.fileVersion4_1) writeV4(stream: headerStream) - Diag.verbose("kdbx4.1 header written OK") + Logger.mainLog.trace("kdbx4.1 header written OK") } let headerData = headerStream.data! @@ -580,9 +585,9 @@ final class Header2: Eraseable { func writeInner(to stream: ByteArray.OutputStream) throws { assert(formatVersion >= .v4) - guard let protectedStreamKey = protectedStreamKey else { fatalError() } + guard let protectedStreamKey = protectedStreamKey else { Logger.fatalError("writeInner: failed to assign protectedStreamKey") } - Diag.verbose("Writing kdbx4 inner header") + Logger.mainLog.trace("Writing kdbx4 inner header") stream.write(value: InnerFieldID.innerRandomStreamID.rawValue) stream.write(value: UInt32(MemoryLayout.size(ofValue: innerStreamAlgorithm.rawValue))) stream.write(value: innerStreamAlgorithm.rawValue) @@ -595,7 +600,7 @@ final class Header2: Eraseable { #endif for binaryID in database.binaries.keys.sorted() { - Diag.verbose("Writing a binary") + Logger.mainLog.trace("Writing a binary") let binary = database.binaries[binaryID]! let data: ByteArray @@ -603,7 +608,7 @@ final class Header2: Eraseable { do { data = try binary.data.gunzipped() } catch { - Diag.error("Failed to uncompress attachment data [message: \(error.localizedDescription)]") + Logger.mainLog.error("Failed to uncompress attachment data [message: \(error.localizedDescription, privacy: .public)]") throw HeaderError.binaryUncompressionError(reason: error.localizedDescription) } } else { @@ -617,11 +622,11 @@ final class Header2: Eraseable { } stream.write(value: InnerFieldID.end.rawValue) stream.write(value: UInt32(0)) - Diag.verbose("Inner header written OK") + Logger.mainLog.trace("Inner header written OK") } internal func randomizeSeeds() throws { - Diag.verbose("Randomizing the seeds") + Logger.mainLog.trace("Randomizing the seeds") fields[.masterSeed] = try CryptoManager.getRandomBytes(count: SHA256_SIZE) fields[.encryptionIV] = try CryptoManager.getRandomBytes(count: dataCipher.initialVectorSize) protectedStreamKey = try CryptoManager.getRandomBytes(count: 64) diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/KeyHelper2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/KeyHelper2.swift index 8530bec..3bd79cd 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/KeyHelper2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/KeyHelper2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log final class KeyHelper2: KeyHelper { @@ -19,18 +20,18 @@ final class KeyHelper2: KeyHelper { var preKey = ByteArray.empty() if hasPassword { - Diag.info("Using password") + Logger.mainLog.info("Using password") preKey = ByteArray.concat(preKey, passwordData.sha256) } if hasKeyFile { - Diag.info("Using key file") + Logger.mainLog.info("Using key file") preKey = ByteArray.concat( preKey, try processKeyFile(keyFileData: keyFileData) ) } if preKey.isEmpty { - Diag.warning("All key components are empty after being checked.") + Logger.mainLog.warning("All key components are empty after being checked.") } return preKey } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Meta2.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Meta2.swift index dfb63a1..685dc97 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Meta2.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/kp2/Meta2.swift @@ -1,4 +1,5 @@ import Foundation +import os.log final class Meta2: Eraseable { public static let generatorName = "Kee Vault 2" @@ -23,7 +24,7 @@ final class Meta2: Eraseable { mutating func load(xml: AEXMLElement) throws { assert(xml.name == Xml2.memoryProtection) - Diag.verbose("Loading XML: memory protection") + Logger.mainLog.trace("Loading XML: memory protection") erase() for tag in xml.children { @@ -39,7 +40,7 @@ final class Meta2: Eraseable { case Xml2.protectNotes: isProtectNotes = Bool(string: tag.value) default: - Diag.error("Unexpected XML tag in Meta/MemoryProtection: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Meta/MemoryProtection: \(tag.name)") throw Xml2.ParsingError.unexpectedTag( actual: tag.name, expected: "Meta/MemoryProtection/*") @@ -48,7 +49,7 @@ final class Meta2: Eraseable { } func toXml() -> AEXMLElement { - Diag.verbose("Generating XML: memory protection") + Logger.mainLog.trace("Generating XML: memory protection") let xmlMP = AEXMLElement(name: Xml2.memoryProtection) xmlMP.addChild( name: Xml2.protectTitle, @@ -174,22 +175,22 @@ final class Meta2: Eraseable { timeParser: Database2XMLTimeParser ) throws { assert(xml.name == Xml2.meta) - Diag.verbose("Loading XML: meta") + Logger.mainLog.trace("Loading XML: meta") erase() for tag in xml.children { switch tag.name { case Xml2.generator: self.generator = tag.value ?? "" - Diag.info("Database was last edited by: \(generator)") + Logger.mainLog.info("Database was last edited by: \(self.generator, privacy: .public)") case Xml2.settingsChanged: guard formatVersion >= .v4 else { - Diag.error("Found \(tag.name) tag in non-V4 database") + Logger.mainLog.error("Found \(tag.name, privacy: .public) tag in non-V4 database") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: nil) } self.settingsChangedTime = timeParser.xmlStringToDate(tag.value) ?? Date.now case Xml2.headerHash: - Diag.warning("Found \(tag.name) tag in non-V3 database. Ignoring") + Logger.mainLog.warning("Found \(tag.name, privacy: .public) tag in non-V3 database. Ignoring") continue case Xml2.databaseName: self.databaseName = tag.value ?? "" @@ -219,10 +220,10 @@ final class Meta2: Eraseable { self.masterKeyChangeForceOnce = Bool(string: tag.value) case Xml2.memoryProtection: try memoryProtection.load(xml: tag) - Diag.verbose("Memory protection loaded OK") + Logger.mainLog.trace("Memory protection loaded OK") case Xml2.customIcons: try loadCustomIcons(xml: tag, timeParser: timeParser) - Diag.verbose("Custom icons loaded OK [count: \(customIcons.count)]") + Logger.mainLog.trace("Custom icons loaded OK [count: \(self.customIcons.count, privacy: .public)]") case Xml2.recycleBinEnabled: self.isRecycleBinEnabled = Bool(string: tag.value) case Xml2.recycleBinUUID: @@ -244,7 +245,7 @@ final class Meta2: Eraseable { self.lastTopVisibleGroupUUID = UUID(base64Encoded: tag.value) ?? UUID.ZERO case Xml2.binaries: try loadBinaries(xml: tag, formatVersion: formatVersion, streamCipher: streamCipher) - Diag.verbose("Binaries loaded OK [count: \(database.binaries.count)]") + Logger.mainLog.trace("Binaries loaded OK [count: \(self.database.binaries.count, privacy: .public)]") case Xml2.customData: try customData.load( xml: tag, @@ -252,9 +253,9 @@ final class Meta2: Eraseable { timeParser: timeParser, xmlParentName: "Meta" ) - Diag.verbose("Custom data loaded OK [count: \(customData.count)]") + Logger.mainLog.trace("Custom data loaded OK [count: \(self.customData.count, privacy: .public)]") default: - Diag.error("Unexpected XML tag in Meta: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Meta: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: "Meta/*") } } @@ -262,16 +263,16 @@ final class Meta2: Eraseable { func loadCustomIcons(xml: AEXMLElement, timeParser: Database2XMLTimeParser) throws { assert(xml.name == Xml2.customIcons) - Diag.verbose("Loading XML: custom icons") + Logger.mainLog.trace("Loading XML: custom icons") for tag in xml.children { switch tag.name { case Xml2.icon: let icon = CustomIcon2() try icon.load(xml: tag, timeParser: timeParser) customIcons.append(icon) - Diag.verbose("Custom icon loaded OK") + Logger.mainLog.trace("Custom icon loaded OK") default: - Diag.error("Unexpected XML tag in Meta/CustomIcons: \(tag.name)") + Logger.mainLog.error("Unexpected XML tag in Meta/CustomIcons: \(tag.name)") throw Xml2.ParsingError.unexpectedTag( actual: tag.name, expected: "Meta/CustomIcons/*") @@ -286,10 +287,10 @@ final class Meta2: Eraseable { ) throws { assert(xml.name == Xml2.binaries) if let tag = xml.children.first { - Diag.error("Unexpected XML content in V4 Meta/Binaries: \(tag.name)") + Logger.mainLog.error("Unexpected XML content in V4 Meta/Binaries: \(tag.name)") throw Xml2.ParsingError.unexpectedTag(actual: tag.name, expected: nil) } else { - Diag.warning("Found empty Meta/Binaries in a V4 database, ignoring.") + Logger.mainLog.warning("Found empty Meta/Binaries in a V4 database, ignoring.") } return } @@ -325,7 +326,7 @@ final class Meta2: Eraseable { formatVersion: Database2.FormatVersion, timeFormatter: Database2XMLTimeFormatter ) throws -> AEXMLElement { - Diag.verbose("Generating XML: meta") + Logger.mainLog.trace("Generating XML: meta") let xmlMeta = AEXMLElement(name: Xml2.meta) xmlMeta.addChild(name: Xml2.generator, value: Meta2.generatorName) @@ -429,10 +430,10 @@ final class Meta2: Eraseable { internal func binariesToXml(streamCipher: StreamCipher) throws -> AEXMLElement? { if database.binaries.isEmpty { - Diag.verbose("No binaries in Meta") + Logger.mainLog.trace("No binaries in Meta") return nil } else { - Diag.verbose("Generating XML: meta binaries") + Logger.mainLog.trace("Generating XML: meta binaries") let xmlBinaries = AEXMLElement(name: Xml2.binaries) for binaryID in database.binaries.keys.sorted() { let binary = database.binaries[binaryID]! diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/totp/TOTPGeneratorFactory.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/totp/TOTPGeneratorFactory.swift index 983d9f8..02373ab 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/totp/TOTPGeneratorFactory.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/totp/TOTPGeneratorFactory.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class TOTPGeneratorFactory { @@ -27,7 +28,7 @@ public class TOTPGeneratorFactory { private static func parseSingleFieldFormat(_ paramString: String) -> TOTPGenerator? { let trimmedParamString = paramString.trimmingCharacters(in: .whitespacesAndNewlines) guard let uriComponents = URLComponents(string: trimmedParamString) else { - Diag.warning("Unexpected OTP field format") + Logger.mainLog.warning("Unexpected OTP field format") return nil } @@ -37,7 +38,7 @@ public class TOTPGeneratorFactory { if KeeOtpFormat.isMatching(scheme: uriComponents.scheme, host: uriComponents.host) { return KeeOtpFormat.parse(paramString) } - Diag.warning("Unrecognized OTP field format") + Logger.mainLog.warning("Unrecognized OTP field format") return nil } @@ -80,7 +81,7 @@ fileprivate class GAuthFormat: SingleFieldFormat { uriComponents.host == host, let queryItems = uriComponents.queryItems else { - Diag.warning("OTP URI has unexpected format") + Logger.mainLog.warning("OTP URI has unexpected format") return nil } @@ -92,12 +93,12 @@ fileprivate class GAuthFormat: SingleFieldFormat { let seedData = base32DecodeToData(seedString), !seedData.isEmpty else { - Diag.warning("OTP parameter cannot be parsed [parameter: \(seedParam)]") + Logger.mainLog.warning("OTP parameter cannot be parsed [parameter: \(seedParam)]") return nil } guard let timeStep = Int(params[timeStepParam] ?? "\(defaultTimeStep)") else { - Diag.warning("OTP parameter cannot be parsed [parameter: \(timeStepParam)]") + Logger.mainLog.warning("OTP parameter cannot be parsed [parameter: \(timeStepParam)]") return nil } @@ -111,14 +112,14 @@ fileprivate class GAuthFormat: SingleFieldFormat { var algorithm: TOTPHashAlgorithm? if let algorithmString = params[algorithmParam] { guard let _algorithm = TOTPHashAlgorithm.fromString(algorithmString) else { - Diag.warning("OTP algorithm is not supported [algorithm: \(algorithmString)]") + Logger.mainLog.warning("OTP algorithm is not supported [algorithm: \(algorithmString, privacy: .public)]") return nil } algorithm = _algorithm } guard let length = Int(params[lengthParam] ?? "\(defaultLength)") else { - Diag.warning("OTP parameter cannot be parsed [parameter: \(lengthParam)]") + Logger.mainLog.warning("OTP parameter cannot be parsed [parameter: \(lengthParam)]") return nil } @@ -158,33 +159,33 @@ fileprivate class KeeOtpFormat: SingleFieldFormat { let seedData = base32DecodeToData(seedString), !seedData.isEmpty else { - Diag.warning("OTP parameter cannot be parsed [parameter: \(seedParam)]") + Logger.mainLog.warning("OTP parameter cannot be parsed [parameter: \(seedParam)]") return nil } if let type = params[typeParam], type.caseInsensitiveCompare(supportedType) != .orderedSame { - Diag.warning("OTP type is not suppoorted [type: \(type)]") + Logger.mainLog.warning("OTP type is not suppoorted [type: \(type)]") return nil } var algorithm: TOTPHashAlgorithm? if let algorithmString = params[algorithmParam] { guard let _algorithm = TOTPHashAlgorithm.fromString(algorithmString) else { - Diag.warning("OTP algorithm is not supported [algorithm: \(algorithmString)]") + Logger.mainLog.warning("OTP algorithm is not supported [algorithm: \(algorithmString)]") return nil } algorithm = _algorithm } guard let timeStep = Int(params[timeStepParam] ?? "\(defaultTimeStep)") else { - Diag.warning("OTP parameter cannot be parsed [parameter: \(timeStepParam)]") + Logger.mainLog.warning("OTP parameter cannot be parsed [parameter: \(timeStepParam)]") return nil } guard let length = Int(params[lengthParam] ?? "\(defaultLength)") else { - Diag.warning("OTP parameter cannot be parsed [parameter: \(lengthParam)]") + Logger.mainLog.warning("OTP parameter cannot be parsed [parameter: \(lengthParam)]") return nil } @@ -203,24 +204,24 @@ fileprivate class SplitFieldFormat { static func parse(seedString: String, settingsString: String?) -> TOTPGenerator? { guard let seed = parseSeedString(seedString) else { - Diag.warning("Unrecognized TOTP seed format") + Logger.mainLog.warning("Unrecognized TOTP seed format") return nil } let settingsString = settingsString ?? SplitFieldFormat.defaultSettingsValue let settings = settingsString.split(separator: ";") if settings.count > 2 { - Diag.verbose("Found redundant TOTP settings, ignoring [expected: 2, got: \(settings.count)]") + Logger.mainLog.trace("Found redundant TOTP settings, ignoring [expected: 2, got: \(settings.count)]") } else if settings.count < 2 { - Diag.warning("Insufficient TOTP settings number [expected: 2, got: \(settings.count)]") + Logger.mainLog.warning("Insufficient TOTP settings number [expected: 2, got: \(settings.count)]") return nil } guard let timeStep = Int(settings[0]) else { - Diag.warning("Failed to parse TOTP time step as Int") + Logger.mainLog.warning("Failed to parse TOTP time step as Int") return nil } guard timeStep > 0 else { - Diag.warning("Invalid TOTP time step value: \(timeStep)") + Logger.mainLog.warning("Invalid TOTP time step value: \(timeStep)") return nil } @@ -234,7 +235,7 @@ fileprivate class SplitFieldFormat { } else if settings[1] == TOTPGeneratorSteam.typeSymbol { return TOTPGeneratorSteam(seed: seed, timeStep: timeStep) } else { - Diag.warning("Unexpected TOTP size or type: '\(settings[1])'") + Logger.mainLog.warning("Unexpected TOTP size or type: '\(settings[1])'") return nil } } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/util/ByteArray.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/util/ByteArray.swift index 5662c00..de33b24 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/util/ByteArray.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/util/ByteArray.swift @@ -1,4 +1,5 @@ import Foundation +import os.log public class ByteArray: Eraseable, Cloneable, Codable, CustomDebugStringConvertible { @@ -138,7 +139,7 @@ public class ByteArray: Eraseable, Cloneable, Codable, CustomDebugStringConverti subscript (index: Int) -> UInt8 { get { return bytes[index] } - set { bytes[index] = newValue } + // Need to invalidate hash caches if we want this to be possible: set { bytes[index] = newValue } } subscript (range: CountableRange) -> ByteArray { return ByteArray(bytes: self.bytes[range]) @@ -283,7 +284,9 @@ public class ByteArray: Eraseable, Cloneable, Codable, CustomDebugStringConverti } public static func empty() -> ByteArray { - return ByteArray(bytes: []) + // Using [UInt8]() instead of [] in case this suffers from same Swift bug + // where after the first execution of the autofill extension, [1] becomes [0] + return ByteArray(bytes: [UInt8]()) } public func append(_ value: UInt8) { diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/util/Eraseable.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/util/Eraseable.swift index 6e9ab66..88b74e1 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/util/Eraseable.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/util/Eraseable.swift @@ -28,6 +28,10 @@ extension Array where Element: Eraseable { public extension Array where Element == UInt8 { mutating func erase() { + // Hypothesis: + // erasing a native Array messes up Swift somehow. Maybe some compiler optimisations that happen + // only in Release mode will point all instances of array literals ([], [0], [1], etc.) to the + // same memory address and the unsafe mutations then screw everything up from that point onwards withUnsafeBufferPointer { let mutatablePointer = UnsafeMutableRawPointer(mutating: $0.baseAddress!) memset_s(mutatablePointer, $0.count, 0, $0.count) diff --git a/ios/KdbxSwift/Sources/KdbxSwift/db/util/VarDict.swift b/ios/KdbxSwift/Sources/KdbxSwift/db/util/VarDict.swift index 4b9138b..7400b8e 100755 --- a/ios/KdbxSwift/Sources/KdbxSwift/db/util/VarDict.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/db/util/VarDict.swift @@ -59,9 +59,11 @@ public class VarDict: Eraseable { self.type = type self.data = data } - init(value: Bool) { - self.init(type: .Bool, data: ByteArray(bytes: [value ? 1 : 0])) - } +// Below may suffer from same bug where 0 == 1 in Database2 deriveMasterKey when array +// literal is assigned to a new ByteArray so thoroughly test before using! +// init(value: Bool) { +// self.init(type: .Bool, data: ByteArray(bytes: [value ? 1 : 0])) +// } init(value: UInt32) { self.init(type: .UInt32, data: value.data) } diff --git a/ios/KdbxSwift/Sources/KdbxSwift/util/Diag.swift b/ios/KdbxSwift/Sources/KdbxSwift/util/Diag.swift deleted file mode 100755 index 803193b..0000000 --- a/ios/KdbxSwift/Sources/KdbxSwift/util/Diag.swift +++ /dev/null @@ -1,147 +0,0 @@ -import Foundation - -public class Diag { - public enum Level: Int { - case verbose = 0 - case debug = 1 - case info = 2 - case warning = 3 - case error = 4 - public var asString: String { - switch self { - case .verbose: - return "(V)" - case .debug: - return "(D)" - case .info: - return "(I)" - case .warning: - return "(W)" - case .error: - return "(E)" - } - } - } - - public struct Item { - public var timestamp: TimeInterval - public var level: Level - public var message: String - public var file: String - public var function: String - public var line: Int - public func toString() -> String { - return "\(String(format: "%.3f", timestamp)) \(level.asString) \t\(file):\(line) \t\(function) \t\(message)" - } - } - - private static let level = Level.debug - private static let instance = Diag() - private let queue = DispatchQueue(label: "com.keevault.unused.diagnostics.stub") - private var items = [Item]() - private var startTime: TimeInterval = Date.timeIntervalSinceReferenceDate - - public class func verbose(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - guard Diag.level.rawValue <= Level.verbose.rawValue else { return } - let item = Item( - timestamp: Date.timeIntervalSinceReferenceDate - instance.startTime, - level: .verbose, - message: message, - file: prettifyFileName(file), - function: prettifyFunctionName(function), - line: line) - instance.add(item: item) - } - public class func debug(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - guard Diag.level.rawValue <= Level.debug.rawValue else { return } - let item = Item( - timestamp: Date.timeIntervalSinceReferenceDate - instance.startTime, - level: .debug, - message: message, - file: prettifyFileName(file), - function: prettifyFunctionName(function), - line: line) - instance.add(item: item) - } - public class func info(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - guard Diag.level.rawValue <= Level.info.rawValue else { return } - let item = Item( - timestamp: Date.timeIntervalSinceReferenceDate - instance.startTime, - level: .info, - message: message, - file: prettifyFileName(file), - function: prettifyFunctionName(function), - line: line) - instance.add(item: item) - } - public class func warning(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - guard Diag.level.rawValue <= Level.warning.rawValue else { return } - let item = Item( - timestamp: Date.timeIntervalSinceReferenceDate - instance.startTime, - level: .warning, - message: message, - file: prettifyFileName(file), - function: prettifyFunctionName(function), - line: line) - instance.add(item: item) - } - public class func error(_ message: String, file: String = #file, function: String = #function, line: Int = #line) { - guard Diag.level.rawValue <= Level.error.rawValue else { return } - let item = Item( - timestamp: Date.timeIntervalSinceReferenceDate - instance.startTime, - level: .error, - message: message, - file: prettifyFileName(file), - function: prettifyFunctionName(function), - line: line) - instance.add(item: item) - } - - public class func clear() { - instance.startTime = Date.timeIntervalSinceReferenceDate - instance.items.removeAll(keepingCapacity: false) - } - - private func add(item: Item) { - queue.async { - print(item.toString()) - self.items.append(item) - } - } - - private static func prettifyFileName(_ fName: String) -> String { - let url = URL(fileURLWithPath: fName, isDirectory: false) - return url.lastPathComponent - } - - private static func prettifyFunctionName(_ fName: String) -> String { - if fName.contains("(") { - return fName - } else { - return fName + "()" - } - } - - public static func itemsSnapshot() -> Array { - return Array(instance.items) - } - - public static func toString() -> String { - return instance._toString() - } - - private func _toString() -> String { - var lines = [String]() - queue.sync { - for item in self.items { - lines.append(item.toString()) - } - } - return lines.joined(separator: "\n") - } - - public static func isDeepDebugMode() -> Bool { - return false - } - -} diff --git a/ios/KdbxSwift/Sources/KdbxSwift/util/Extensions.swift b/ios/KdbxSwift/Sources/KdbxSwift/util/Extensions.swift index 491ed88..166ec06 100644 --- a/ios/KdbxSwift/Sources/KdbxSwift/util/Extensions.swift +++ b/ios/KdbxSwift/Sources/KdbxSwift/util/Extensions.swift @@ -1,4 +1,5 @@ import Foundation +import os.log extension StringProtocol { func base64ToBase64url() -> String { @@ -145,7 +146,7 @@ extension UUID { internal var data: ByteArray { var bytes = Array(repeating: 0, count: UUID.byteWidth) guard let nsuuid = NSUUID(uuidString: self.uuidString) else { - fatalError() + Logger.fatalError("failed to create NSUUID") } nsuuid.getBytes(&bytes) return ByteArray(bytes: bytes) diff --git a/ios/KdbxSwift/Sources/KdbxSwift/util/Logger.swift b/ios/KdbxSwift/Sources/KdbxSwift/util/Logger.swift new file mode 100755 index 0000000..6428612 --- /dev/null +++ b/ios/KdbxSwift/Sources/KdbxSwift/util/Logger.swift @@ -0,0 +1,13 @@ +import Foundation +import os.log + +extension Logger { + private static var subsystem = Bundle.main.bundleIdentifier! + + public static let mainLog = Logger(subsystem: subsystem, category: "main") + + public static func fatalError(_ message:String) -> Never { + mainLog.fault("\(message)") + Swift.fatalError(message) + } +} diff --git a/ios/fastlane/Fastfile b/ios/fastlane/Fastfile index 7668031..94eb181 100644 --- a/ios/fastlane/Fastfile +++ b/ios/fastlane/Fastfile @@ -195,4 +195,53 @@ platform :ios do delete_temp_keychain(keychain_name) end end + + ### Warning!! Only thing that determines whether this points at beta or prod servers is + # the command most recently executed! + # + # Remember to revert the changes made to ios/Runner.xcodeproj/project.pbxproj before comitting! + desc "Push a new beta build to local device" + lane :beta_local_device do + keychain_name = TEMP_KEYCHAIN_USER + keychain_password = TEMP_KEYCHAIN_PASSWORD + + begin + ensure_temp_keychain(keychain_name, keychain_password) + + match( + type: "adhoc", + app_identifier: ['com.keevault.keevault.beta', 'com.keevault.keevault.beta.autofill'], + readonly: true, + keychain_name: keychain_name, + keychain_password: keychain_password + ) + update_project_provisioning( + xcodeproj: "Runner.xcodeproj", + profile: ENV['sigh_com.keevault.keevault.beta_adhoc_profile-path'], + target_filter: ".*Runner.*", + build_configuration: "Release" + ) + update_project_provisioning( + xcodeproj: "Runner.xcodeproj", + profile: ENV['sigh_com.keevault.keevault.beta.autofill_adhoc_profile-path'], + target_filter: ".*KeeVaultAutofill.*", + build_configuration: "Release" + ) + build_app( + workspace: "Runner.xcworkspace", + scheme: "Runner", + configuration: 'Release', + export_method: 'ad-hoc', + export_options: { + provisioningProfiles: { + 'com.keevault.keevault.beta' => 'match AdHoc com.keevault.keevault.beta', + 'com.keevault.keevault.beta.autofill' => 'match AdHoc com.keevault.keevault.beta.autofill' + } + } + ) + install_on_device() + ensure + delete_temp_keychain(keychain_name) + end + end end diff --git a/ios/fastlane/README.md b/ios/fastlane/README.md index 8624808..c47ee7a 100644 --- a/ios/fastlane/README.md +++ b/ios/fastlane/README.md @@ -55,6 +55,14 @@ Push a new beta build to test service Push a new prod build to testflight +### ios beta_local_device + +```sh +[bundle exec] fastlane ios beta_local_device +``` + +Push a new beta build to local device + ---- This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. diff --git a/pubspec.yaml b/pubspec.yaml index 4ac02ba..7fec8a5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.1.1+49 +version: 1.1.1+50 environment: sdk: '>=2.18.0 <3.0.0'