diff --git a/enzevalos_iphone/AppDelegate.swift b/enzevalos_iphone/AppDelegate.swift index 2aed59adef8927d0bf8543b8015ed93751dbb289..e7fb1d0c138b56f736f05e1f557bf20bfebdd8e0 100644 --- a/enzevalos_iphone/AppDelegate.swift +++ b/enzevalos_iphone/AppDelegate.swift @@ -19,7 +19,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var contactStore = CNContactStore() var mailHandler = MailHandler() var orientationLock = UIInterfaceOrientationMask.allButUpsideDown - var logging = false func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. diff --git a/enzevalos_iphone/DataHandler.swift b/enzevalos_iphone/DataHandler.swift index c952bdf253cefba653efdca4adc0e1f72be8e7d0..c8f90972b6e0069ac41cc7dcaa2f2a2ea98bcb2f 100644 --- a/enzevalos_iphone/DataHandler.swift +++ b/enzevalos_iphone/DataHandler.swift @@ -763,6 +763,30 @@ class DataHandler { } return createPseudonymMailAddress(mailAddress: mailAddress) } + + private func createPseudonymKey(keyID: String) -> PseudonymKey { + let pseudonymKey = NSEntityDescription.insertNewObject(forEntityName: "PseudonymKey", into: managedObjectContext) as! PseudonymKey + var found = false + while found { + let pseudo = String.random() + let response = find("PseudonymKey", type: "pseudonym", search: pseudo) as? [PseudonymKey] + if (response ?? []).count == 0 || response![0].pseudonym == "" { + pseudonymKey.pseudonym = pseudo + found = true + } + } + pseudonymKey.keyID = keyID.lowercased() + save() + return pseudonymKey + } + + func getPseudonymKey(keyID: String) -> PseudonymKey { + let result = find("PseudonymKey", type: "keyID", search: keyID.lowercased()) + if let list = result as? [PseudonymKey], list.count > 0 { + return list[0] + } + return createPseudonymKey(keyID: keyID) + } private func readMails() -> [PersistentMail] { var mails = [PersistentMail]() diff --git a/enzevalos_iphone/Logger.swift b/enzevalos_iphone/Logger.swift index 0ec068b71950b4c8bd54c9a3981a787dc5add194..4e410b56198de527ebcd9a02de979021a1fd12be 100644 --- a/enzevalos_iphone/Logger.swift +++ b/enzevalos_iphone/Logger.swift @@ -10,70 +10,142 @@ import Foundation class Logger { + static var logging = false + static let defaultFileName = "log.json" - static func log(mailSent event: Event, from: String, to: [String], cc: [String], bcc: [String], bodyLength: Int, isEncrypted: Bool, decryptedBodyLength: Int, decryptedWithOldPrivateKey: Bool = false, isSigned: Bool, isCorrectlySigned: Bool = true, signingKeyID: String, myKeyID: String, secureAddresses: [String] = [], encryptedForKeyIDs: [String] = []) { - - event.append(key: "type", value: LoggingEventType.mailSent) - event.append(key: "from", value: from) - event.append(key: "to", value: Logger.resolve(mailAddresses: to)) - event.append(key: "cc", value: Logger.resolve(mailAddresses: cc)) - event.append(key: "bcc", value: Logger.resolve(mailAddresses: bcc)) - event.append(key: "bodyLength", value: bodyLength) - event.append(key: "isEncrypted", value: isEncrypted) - event.append(key: "decryptedBodyLength", value: decryptedBodyLength) - event.append(key: "decryptedWithOldPrivateKey", value: decryptedWithOldPrivateKey) - event.append(key: "isSigned", value: isSigned) - event.append(key: "isCorrectlySigned", value: isCorrectlySigned) - event.append(key: "signingKeyID", value: Logger.resolve(keyID: signingKeyID)) - event.append(key: "myKeyID", value: Logger.resolve(keyID: myKeyID)) - event.append(key: "secureAddresses", value: Logger.resolve(mailAddresses:secureAddresses)) //means the addresses, which received a secure mail - event.append(key: "encryptedForKeyIDs", value: Logger.resolve(keyIDs: encryptedForKeyIDs)) - - } - - static func log(mailRead event: Event, from: String, to: [String], cc: [String], bcc: [String], bodyLength: Int, isEncrypted: Bool, decryptedBodyLength: Int, decryptedWithOldPrivateKey: Bool = false, isSigned: Bool, isCorrectlySigned: Bool = true, signingKeyID: String, myKeyID: String, secureAddresses: [String] = [], encryptedForKeyIDs: [String] = []) { - - event.append(key: "type", value: LoggingEventType.mailRead) - event.append(key: "from", value: from) - event.append(key: "to", value: Logger.resolve(mailAddresses: to)) - event.append(key: "cc", value: Logger.resolve(mailAddresses: cc)) - event.append(key: "bcc", value: Logger.resolve(mailAddresses: bcc)) - event.append(key: "bodyLength", value: bodyLength) - event.append(key: "isEncrypted", value: isEncrypted) - event.append(key: "decryptedBodyLength", value: decryptedBodyLength) - event.append(key: "decryptedWithOldPrivateKey", value: decryptedWithOldPrivateKey) - event.append(key: "isSigned", value: isSigned) - event.append(key: "isCorrectlySigned", value: isCorrectlySigned) - event.append(key: "signingKeyID", value: Logger.resolve(keyID: signingKeyID)) - event.append(key: "myKeyID", value: Logger.resolve(keyID: myKeyID)) - event.append(key: "secureAddresses", value: secureAddresses) //could mean the addresses, in this mail we have a key for - event.append(key: "encryptedForKeyIDs", value: Logger.resolve(keyIDs: encryptedForKeyIDs)) - - } - - static func log(mailRead event: Event, mail: PersistentMail) { - - event.append(key: "type", value: LoggingEventType.mailRead) - event.append(key: "from", value: mail.from) - event.append(key: "to", value: Logger.resolve(mailAddresses: mail.to)) - event.append(key: "cc", value: Logger.resolve(mailAddresses: mail.cc ?? NSSet())) - event.append(key: "bcc", value: Logger.resolve(mailAddresses: mail.bcc ?? NSSet())) - event.append(key: "bodyLength", value: (mail.body ?? "").count) - event.append(key: "isEncrypted", value: mail.isEncrypted) - event.append(key: "decryptedBodyLength", value: (mail.decryptedBody ?? "").count) - event.append(key: "decryptedWithOldPrivateKey", value: mail.decryptedWithOldPrivateKey) - event.append(key: "isSigned", value: mail.isSigned) - event.append(key: "isCorrectlySigned", value: mail.isCorrectlySigned) + static fileprivate func plainLogDict() -> [String: Any] { + var fields: [String: Any] = [:] + let now = Date() + //TODO add uuid here + fields["timestamp"] = now.description + return fields + } + + static fileprivate func dictToJSON(fields: [String: Any]) -> String { + do { + let jsonData = try JSONSerialization.data(withJSONObject: fields) + if let json = String(data: jsonData, encoding: String.Encoding.utf8) { + return json + } + return "" + } catch { + return "{\"error\":\"json conversion failed\"}" + } + } + + static func log(sent from: String, to: [String], cc: [String], bcc: [String], bodyLength: Int, isEncrypted: Bool, decryptedBodyLength: Int, decryptedWithOldPrivateKey: Bool = false, isSigned: Bool, isCorrectlySigned: Bool = true, signingKeyID: String, myKeyID: String, secureAddresses: [String] = [], encryptedForKeyIDs: [String] = []) { + + if !logging { + return + } + + var event = plainLogDict() + + event["type"] = LoggingEventType.mailSent + event["from"] = from + event["to"] = Logger.resolve(mailAddresses: to) + event["cc"] = Logger.resolve(mailAddresses: cc) + event["bcc"] = Logger.resolve(mailAddresses: bcc) + event["bodyLength"] = bodyLength + event["isEncrypted"] = isEncrypted + event["decryptedBodyLength"] = decryptedBodyLength + event["decryptedWithOldPrivateKey"] = decryptedWithOldPrivateKey + event["isSigned"] = isSigned + event["isCorrectlySigned"] = isCorrectlySigned + event["signingKeyID"] = Logger.resolve(keyID: signingKeyID) + event["myKeyID"] = Logger.resolve(keyID: myKeyID) + event["secureAddresses"] = Logger.resolve(mailAddresses:secureAddresses) //means the addresses, which received a secure mail + event["encryptedForKeyIDs"] = Logger.resolve(keyIDs: encryptedForKeyIDs) + + saveToDisk(json: dictToJSON(fields: event)) + } + + static func log(read from: String, to: [String], cc: [String], bcc: [String], bodyLength: Int, isEncrypted: Bool, decryptedBodyLength: Int, decryptedWithOldPrivateKey: Bool = false, isSigned: Bool, isCorrectlySigned: Bool = true, signingKeyID: String, myKeyID: String, secureAddresses: [String] = [], encryptedForKeyIDs: [String] = []) { + + if !logging { + return + } + + var event = plainLogDict() + + event["type"] = LoggingEventType.mailRead + event["from"] = from + event["to"] = Logger.resolve(mailAddresses: to) + event["cc"] = Logger.resolve(mailAddresses: cc) + event["bcc"] = Logger.resolve(mailAddresses: bcc) + event["bodyLength"] = bodyLength + event["isEncrypted"] = isEncrypted + event["decryptedBodyLength"] = decryptedBodyLength + event["decryptedWithOldPrivateKey"] = decryptedWithOldPrivateKey + event["isSigned"] = isSigned + event["isCorrectlySigned"] = isCorrectlySigned + event["signingKeyID"] = Logger.resolve(keyID: signingKeyID) + event["myKeyID"] = Logger.resolve(keyID: myKeyID) + event["secureAddresses"] = secureAddresses //could mean the addresses, in this mail we have a key for + event["encryptedForKeyIDs"] = Logger.resolve(keyIDs: encryptedForKeyIDs) + + saveToDisk(json: dictToJSON(fields: event)) + } + + static func log(read mail: PersistentMail) { + var event = plainLogDict() + + if !logging { + return + } + + event["type"] = LoggingEventType.mailRead + event["from"] = mail.from + event["to"] = Logger.resolve(mailAddresses: mail.to) + event["cc"] = Logger.resolve(mailAddresses: mail.cc ?? NSSet()) + event["bcc"] = Logger.resolve(mailAddresses: mail.bcc ?? NSSet()) + event["bodyLength"] = (mail.body ?? "").count + event["isEncrypted"] = mail.isEncrypted + event["decryptedBodyLength"] = (mail.decryptedBody ?? "").count + event["decryptedWithOldPrivateKey"] = mail.decryptedWithOldPrivateKey + event["isSigned"] = mail.isSigned + event["isCorrectlySigned"] = mail.isCorrectlySigned + //TODO: + //event["signingKeyID"] = Logger.resolve(keyID: signingKeyID) + //event["myKeyID"] = Logger.resolve(keyID: myKeyID) + + + + //event["secureAddresses"] = secureAddresses //could mean the addresses, in this mail we have a key for + //event["encryptedForKeyIDs"] = Logger.resolve(keyIDs: encryptedForKeyIDs) + + saveToDisk(json: dictToJSON(fields: event)) + } + + static func log(received mail: PersistentMail) { + var event = plainLogDict() + + if !logging { + return + } + + event["type"] = LoggingEventType.mailReceived + event["from"] = mail.from + event["to"] = Logger.resolve(mailAddresses: mail.to) + event["cc"] = Logger.resolve(mailAddresses: mail.cc ?? NSSet()) + event["bcc"] = Logger.resolve(mailAddresses: mail.bcc ?? NSSet()) + event["bodyLength"] = (mail.body ?? "").count + event["isEncrypted"] = mail.isEncrypted + event["decryptedBodyLength"] = (mail.decryptedBody ?? "").count + event["decryptedWithOldPrivateKey"] = mail.decryptedWithOldPrivateKey + event["isSigned"] = mail.isSigned + event["isCorrectlySigned"] = mail.isCorrectlySigned //TODO: - //event.append(key: "signingKeyID", value: Logger.resolve(keyID: signingKeyID)) - //event.append(key: "myKeyID", value: Logger.resolve(keyID: myKeyID)) + //event["signingKeyID"] = Logger.resolve(keyID: signingKeyID) + //event["myKeyID"] = Logger.resolve(keyID: myKeyID) - //event.append(key: "secureAddresses", value: secureAddresses) //could mean the addresses, in this mail we have a key for - //event.append(key: "encryptedForKeyIDs", value: Logger.resolve(keyIDs: encryptedForKeyIDs)) + //event["secureAddresses"] = secureAddresses //could mean the addresses, in this mail we have a key for + //event["encryptedForKeyIDs"] = Logger.resolve(keyIDs: encryptedForKeyIDs) + saveToDisk(json: dictToJSON(fields: event)) } static func logInbox() { @@ -101,8 +173,7 @@ class Logger { //get an pseudonym for a keyID static func resolve(keyID: String) -> String { - //TODO: implement - return "" + return DataHandler.handler.getPseudonymKey(keyID: keyID).pseudonym } //escape the entry of one cell in a csv @@ -215,31 +286,3 @@ class Logger { } } } - -enum LogType: String { - case - unknown = "unknown", //unknown type - key = "key", //If a new key is discovered/created/verified/etc. this should be logged here - mail = "mail", //If a new mail is received or send, this should be logged here - ui = "ui", //If a specific UI-element (e.g warning, error message, info button) is triggered, the event should be logged here - bug = "bug" //Bugs produced by us should log in this type - - /*func append(message: String) { - Logger.log(message: Logger.escape(message: message), type: self) - }*/ -} - -enum MailLog: String { - case - unknown = "unknown", //unknown type - read = "read", //mail was read by participant - sent = "sent", //mail is sent by participant - received = "receive" //mail is received by participant - - func append(mail: PersistentMail) { - /* - Date,type,from,cc,bcc,bodyLength,isEncrypted,unableToDecrypt,decryptBodyLength,decryptedWithOldPrivateKey,isSigned,isCorrectlySigned,keyID,mailLog - */ - Logger.log(generic: mail, message: Logger.escape(message: self.rawValue)) - } -} diff --git a/enzevalos_iphone/LoggingEventType.swift b/enzevalos_iphone/LoggingEventType.swift index 3d238129a7d46760e3af9e3c6957d7b08f15e6ae..95ebc366054b10feb21f7f60b0a3e7eafc5a83a2 100644 --- a/enzevalos_iphone/LoggingEventType.swift +++ b/enzevalos_iphone/LoggingEventType.swift @@ -12,5 +12,6 @@ enum LoggingEventType: String { case unknown = "unknown", mailRead = "mail read", - mailSent = "mail sent" + mailSent = "mail sent", + mailReceived = "mail received" } diff --git a/enzevalos_iphone/MailHandler.swift b/enzevalos_iphone/MailHandler.swift index f7b1ca4d42e81a30a75d7cbd7b605aaeed391312..e0602ca30e71ec87de807c727f262cee7c0b9b2d 100644 --- a/enzevalos_iphone/MailHandler.swift +++ b/enzevalos_iphone/MailHandler.swift @@ -288,6 +288,7 @@ class MailHandler { var allRec: [String] = [] allRec.append(contentsOf: toEntrys) allRec.append(contentsOf: ccEntrys) + allRec.append(contentsOf: bccEntrys) let ordered = orderReceiver(receiver: allRec) @@ -307,8 +308,8 @@ class MailHandler { let cryptoObject = pgp.encrypt(plaintext: "\n" + message, ids: keyIDs, myId:sk.keyID!) if let encData = cryptoObject.chiphertext{ sendData = encData - if AppDelegate.getAppDelegate().logging { - Logger.log(sent: useraddr, to: toEntrys, cc: ccEntrys, bcc: bccEntrys, bodyLength: (String(data: cryptoObject.chiphertext!, encoding: String.Encoding.utf8) ?? "").count, isEncrypted: true, decryptedBodyLength: ("\n"+message).count, isSigned: true, keyID: sk.keyID!, secureAddresses: encPGP.map{$0.mailbox}, otherKeyIDs: keyIDs) + if Logger.logging { + Logger.log(sent: useraddr, to: toEntrys, cc: ccEntrys, bcc: bccEntrys, bodyLength: (String(data: cryptoObject.chiphertext!, encoding: String.Encoding.utf8) ?? "").count, isEncrypted: true, decryptedBodyLength: ("\n"+message).count, decryptedWithOldPrivateKey: false, isSigned: true, isCorrectlySigned: true, signingKeyID: sk.keyID!, myKeyID: sk.keyID!, secureAddresses: encPGP.map{$0.mailbox}, encryptedForKeyIDs: keyIDs) } builder.textBody = "Dies ist verschlüsselt!" @@ -334,6 +335,9 @@ class MailHandler { sendOperation = session.sendOperation(with: sendData, from: userID, recipients: unenc) //TODO handle different callbacks //TODO add logging call here for the case the full email is unencrypted + if unenc.count == allRec.count { + Logger.log(sent: useraddr, to: toEntrys, cc: ccEntrys, bcc: bccEntrys, bodyLength: ("\n"+message).count, isEncrypted: false, decryptedBodyLength: ("\n"+message).count, decryptedWithOldPrivateKey: false, isSigned: false, isCorrectlySigned: false, signingKeyID: "", myKeyID: "", secureAddresses: [], encryptedForKeyIDs: []) + } sendOperation.start(callback) createSendCopy(sendData: sendData) } @@ -695,11 +699,9 @@ class MailHandler { for keyId in newKeyIds{ _ = DataHandler.handler.newPublicKey(keyID: keyId, cryptoType: CryptoScheme.PGP, adr: from.mailbox, autocrypt: false, firstMail: mail) } - if AppDelegate.getAppDelegate().logging { + if Logger.logging { if let mail = mail { Logger.log(received: mail) - } else { - //TODO: log a bug (?) } } if newMailCallback != nil{ diff --git a/enzevalos_iphone/ReadViewController.swift b/enzevalos_iphone/ReadViewController.swift index 64b6d74178ff8724a0534551de5975260d88303f..26b9ee3e339c6bbd055421562c2665792d51759d 100644 --- a/enzevalos_iphone/ReadViewController.swift +++ b/enzevalos_iphone/ReadViewController.swift @@ -56,7 +56,7 @@ class ReadViewController: UITableViewController { answerButton.title = NSLocalizedString("edit", comment: "") } else { answerButton.title = NSLocalizedString("answer", comment: "") - if let mail = mail, AppDelegate.getAppDelegate().logging { + if Logger.logging, let mail = mail { Logger.log(read: mail) } } diff --git a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone.xcdatamodel/contents b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone.xcdatamodel/contents index bdd58b53d07e10987137f285b50761f36a1bc6d8..adbca8e01eabef997059ebbbaf72d5c46340203b 100644 --- a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone.xcdatamodel/contents +++ b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone.xcdatamodel/contents @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="13241" systemVersion="16G1036" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="13533" systemVersion="16G1036" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> <entity name="EnzevalosContact" representedClassName="EnzevalosContact" syncable="YES"> <attribute name="cnidentifier" optional="YES" attributeType="String" syncable="YES"/> <attribute name="color" optional="YES" attributeType="Transformable" customClassName="UIColor" syncable="YES"/> diff --git a/enzevalos_iphoneTests/LoggerTests.swift b/enzevalos_iphoneTests/LoggerTests.swift index 06b5085808cbcc4c7aa07421be98bea791fccb36..cb9bb13092b6bf96bdc052ab5b2e1bd62d719765 100644 --- a/enzevalos_iphoneTests/LoggerTests.swift +++ b/enzevalos_iphoneTests/LoggerTests.swift @@ -35,7 +35,7 @@ class LoggerTests: XCTestCase { func testLogWriting() { let testString = "This is a test String" let testFile = "testLog.json" - Logger.saveToDisk(fileName: testFile, json: testString) + Logger.saveToDisk(json: testString, fileName: testFile) if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {