diff --git a/enzevalos_iphone.xcodeproj/project.pbxproj b/enzevalos_iphone.xcodeproj/project.pbxproj index 190d9b6af48052a2de3716aabdc9bb9864cc8f6e..651e77bc69c10bbecdfa7d1dada48d0dbb99d692 100644 --- a/enzevalos_iphone.xcodeproj/project.pbxproj +++ b/enzevalos_iphone.xcodeproj/project.pbxproj @@ -162,6 +162,7 @@ 478154A921FF3FF400A931EC /* Invitation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478154A821FF3FF400A931EC /* Invitation.swift */; }; 478154AC21FF6A9600A931EC /* Mailbot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478154AB21FF6A9600A931EC /* Mailbot.swift */; }; 478154AE2200641900A931EC /* StudyTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478154AD2200641900A931EC /* StudyTest.swift */; }; + 478AF715222FD5C600AEF69E /* IncomingMail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 478AF714222FD5C600AEF69E /* IncomingMail.swift */; }; 479B5977206914BE00B3944D /* CryptoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 479B5976206914BE00B3944D /* CryptoTests.swift */; }; 479B597820691BE400B3944D /* ObjectivePGP.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 47CEF4EC2052C3E600887CDB /* ObjectivePGP.framework */; }; 479B597920691BFB00B3944D /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 47F867E32052B49800AA832F /* libbz2.tbd */; }; @@ -363,6 +364,7 @@ 478154A821FF3FF400A931EC /* Invitation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Invitation.swift; sourceTree = "<group>"; }; 478154AB21FF6A9600A931EC /* Mailbot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mailbot.swift; sourceTree = "<group>"; }; 478154AD2200641900A931EC /* StudyTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StudyTest.swift; sourceTree = "<group>"; }; + 478AF714222FD5C600AEF69E /* IncomingMail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = IncomingMail.swift; path = mail/IncomingMail.swift; sourceTree = "<group>"; }; 479B5976206914BE00B3944D /* CryptoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoTests.swift; sourceTree = "<group>"; }; 479C649521F2139B00A01071 /* support_pk.asc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = support_pk.asc; sourceTree = "<group>"; }; 479C649821F45DAF00A01071 /* HideShowPasswordTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HideShowPasswordTextField.swift; sourceTree = "<group>"; }; @@ -633,6 +635,7 @@ A1EB057B1D956838008659C1 /* MailHandler.swift */, 477548DD21F5DABE000B22A8 /* MailServerConnectionError.swift */, 47CEAC97222541B40075B7DC /* MailSession.swift */, + 478AF714222FD5C600AEF69E /* IncomingMail.swift */, ); name = mail; sourceTree = "<group>"; @@ -1588,6 +1591,7 @@ 3EC35F2D200376A1008BDF95 /* SendViewController+Invitation.swift in Sources */, 475B00341F7B9565006CDD41 /* Cryptography.swift in Sources */, A1EB057C1D956838008659C1 /* MailHandler.swift in Sources */, + 478AF715222FD5C600AEF69E /* IncomingMail.swift in Sources */, A1EB05881D956879008659C1 /* AddressHandler.swift in Sources */, 472F39701E14F75C009260FB /* DataHandler.swift in Sources */, A1C62E9A2018F716000E5273 /* OnboardingValueState.swift in Sources */, diff --git a/enzevalos_iphone.xcodeproj/project.xcworkspace/xcuserdata/Olli.xcuserdatad/UserInterfaceState.xcuserstate b/enzevalos_iphone.xcodeproj/project.xcworkspace/xcuserdata/Olli.xcuserdatad/UserInterfaceState.xcuserstate index c93b9ff53b8d6cc0260381a2db718b0bf37413fb..cc59415b1fd6ec353f8779ec0721d09d40ad4361 100644 Binary files a/enzevalos_iphone.xcodeproj/project.xcworkspace/xcuserdata/Olli.xcuserdatad/UserInterfaceState.xcuserstate and b/enzevalos_iphone.xcodeproj/project.xcworkspace/xcuserdata/Olli.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/enzevalos_iphone/CryptoObject.swift b/enzevalos_iphone/CryptoObject.swift index 09a631d99e562af1ab692d2b2098f2cf166c0773..e6571a8cc94e198cc4df30ef102f05135922c18a 100644 --- a/enzevalos_iphone/CryptoObject.swift +++ b/enzevalos_iphone/CryptoObject.swift @@ -7,18 +7,19 @@ // import Foundation -enum SignatureState { - case NoSignature - case NoPublicKey - case InvalidSignature - case ValidSignature +enum SignatureState: Int { + + case NoSignature = 0 + case NoPublicKey = 1 + case InvalidSignature = -1 + case ValidSignature = 2 } -enum EncryptionState { - case NoEncryption - case UnableToDecrypt - case ValidEncryptedWithOldKey - case ValidedEncryptedWithCurrentKey +enum EncryptionState: Int { + case NoEncryption = 0 + case UnableToDecrypt = 1 + case ValidEncryptedWithOldKey = 2 + case ValidedEncryptedWithCurrentKey = 3 } public enum CryptoScheme { @@ -89,8 +90,4 @@ public class CryptoObject { self.passcode = nil self.signedAdrs = signedAdrs } - - - - } diff --git a/enzevalos_iphone/Cryptography.swift b/enzevalos_iphone/Cryptography.swift index 26d1991f6d8af322ef81f1cd35b7c1c671940901..2c47db469678e54029685230a0945b803eaade7a 100644 --- a/enzevalos_iphone/Cryptography.swift +++ b/enzevalos_iphone/Cryptography.swift @@ -20,6 +20,6 @@ public protocol Encryption { // operations on keys func encrypt(plaintext: String, ids: [String], myId: String, encryptForMyID: Bool) -> CryptoObject - func decrypt(data: Data, decryptionIDs: [String], verifyIds: [String], fromAdr: String?) -> CryptoObject + func decrypt(data: Data, attachedSignature: Data?, decKeyIDs: [String], signatureIDs: [String], fromAddr: String) -> CryptoObject } diff --git a/enzevalos_iphone/DataHandler.swift b/enzevalos_iphone/DataHandler.swift index 3e9987dfbbf7d6c590f33a38827b54883dad90df..764f7fe63d96c7b20b790fec59ff629769b9a070 100644 --- a/enzevalos_iphone/DataHandler.swift +++ b/enzevalos_iphone/DataHandler.swift @@ -829,7 +829,7 @@ class DataHandler { myfolder.maxID = mail.uid } var record = getKeyRecord(addr: mail.from.mailAddress, keyID: nil, saveRecord: false) - if let signedID = mail.signedKey?.keyID { + if let signedID = mail.signedKey?.keyID, mail.isSecure { record = getKeyRecord(addr: mail.from.mailAddress, keyID: signedID, saveRecord: false) } record.addToPersistentMails(mail) diff --git a/enzevalos_iphone/MailHandler.swift b/enzevalos_iphone/MailHandler.swift index e573b474271f2c0ef947d8a82824ef68cdb003a7..2122368ca1668541d2670e5275ec5a6a32481020 100644 --- a/enzevalos_iphone/MailHandler.swift +++ b/enzevalos_iphone/MailHandler.swift @@ -512,8 +512,8 @@ class MailHandler { return } if let parser = data { - - _ = self.parseMail(parser: parser, record: record, folderPath: folderPath, uid: UInt64(message.uid), flags: message.flags) + let incomingMail = IncomingMail(rawData: parser.data(), uID: UInt64(message.uid), folderPath: folderPath, flags: message.flags) + _ = incomingMail.store(keyRecord: record) } dispatchGroup.leave() @@ -537,342 +537,6 @@ class MailHandler { } } - internal func parseMail(parser: MCOMessageParser, record: KeyRecord?, folderPath: String, uid: UInt64, flags: MCOMessageFlag) -> PersistentMail?{ - var rec: [MCOAddress] = [] - var cc: [MCOAddress] = [] - var autocrypt: Autocrypt? = nil - var newKeyIds = [String]() - var secretKey: String? = nil - var isEnc = false - var html: String - var body: String - var lineArray: [String] - var dec: CryptoObject? = nil - let header = parser.header - let msgID = header?.messageID - let userAgent = header?.userAgent - var references = [String]() - var msgParser = parser - - // 1. parse header - if header?.from == nil { - // Drops mails with no from field. Otherwise it becomes ugly with no ezcontact,fromadress etc. - return nil - } - if let refs = header?.references { - for ref in refs { - if let string = ref as? String { - references.append(string) - } - } - } - if let _ = header?.extraHeaderValue(forName: Autocrypt.AUTOCRYPTHEADER) { - autocrypt = Autocrypt(header: header!) - } - if let _ = header?.extraHeaderValue(forName: Autocrypt.SETUPMESSAGE) { - // TODO: Distinguish between other keys (future work) - return nil - } - if let to = header?.to { - for r in to { - rec.append(r as! MCOAddress) - } - } - if let c = header?.cc { - for r in c { - cc.append(r as! MCOAddress) - } - } - var attachedSignature: Data? - // 2. parse body - for a in (msgParser.attachments())! { - let at = a as! MCOAttachment - if at.mimeType == "application/pgp-encrypted" { - isEnc = true - } - if let signature = MailHandler.extractPGPSignature(attachment: at) { - attachedSignature = signature - } - if isEnc && at.mimeType == "application/octet-stream" { - msgParser = MCOMessageParser(data: at.data) - } - newKeyIds.append(contentsOf: parsePublicKeys(attachment: at)) - if let sk = parseSecretKey(attachment: at) { - secretKey = sk - } - } - body = msgParser.plainTextBodyRenderingAndStripWhitespace(false) - if isEnc { - html = msgParser.plainTextRendering() - lineArray = html.components(separatedBy: "\n") - lineArray.removeFirst(4) - body = lineArray.joined(separator: "\n") - body = body.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) - body.append("\n") - dec = decryptText(body: body, from: header?.from, autocrypt: autocrypt) - if (dec?.plaintext != nil) { - msgParser = MCOMessageParser(data: dec?.decryptedData) - body = parseBody(msgParser: msgParser) - for a in (msgParser.attachments())! { - let at = a as! MCOAttachment - newKeyIds.append(contentsOf: parsePublicKeys(attachment: at)) - if let sk = parseSecretKey(attachment: at) { - secretKey = sk - } - - } - } - } - else if let attachedSig = attachedSignature { - let signedData = MailHandler.extractSignedData(data: msgParser.data()) - let pgp = SwiftPGP() - var keyIds = [String]() - if let sender = header?.from.mailbox { - if let adr = DataHandler.handler.findMailAddress(adr: sender) { - for k in adr.publicKeys { - keyIds.append(k.keyID) - } - } - dec = pgp.verify(data: signedData!, attachedSignature: attachedSig, verifyIds: keyIds, fromAdr: sender) - } - } - else { - body = parseBody(msgParser: msgParser) - if let chipher = findInlinePGP(text: msgParser.plainTextRendering()) { - dec = decryptText(body: chipher, from: header?.from, autocrypt: autocrypt) - if dec != nil { - if let text = dec?.decryptedText { - body = text.removeNewLines() - } - } - } - } - newKeyIds.append(contentsOf: findKeyString(content: body)) - if let header = header, let from = header.from, let date = header.date { - let mail = DataHandler.handler.createMail(uid, sender: from, receivers: rec, cc: cc, time: date, received: true, subject: header.subject ?? "", body: body, flags: flags, record: record, autocrypt: autocrypt, decryptedData: dec, folderPath: folderPath, secretKey: secretKey, references: references, mailagent: userAgent, messageID: msgID) - if let m = mail { - let pgp = SwiftPGP() - if let autoc = autocrypt { - if let publickeys = try? pgp.importKeys(key: autoc.key, pw: nil, isSecretKey: false, autocrypt: true) { - for pk in publickeys { - _ = DataHandler.handler.newPublicKey(keyID: pk, cryptoType: CryptoScheme.PGP, adr: from.mailbox, autocrypt: true, firstMail: mail) - } - } - } - for keyId in newKeyIds { - _ = DataHandler.handler.newPublicKey(keyID: keyId, cryptoType: CryptoScheme.PGP, adr: from.mailbox, autocrypt: false, firstMail: mail) - } - Logger.log(received: m) - } - return mail - } - return nil - } - - - func findSignedDataAndSignature(data: Data) -> (signedData: Data?, signature: Data?) { - var sig: Data? - if let parser = MCOMessageParser.init(data: data) { - for part in parser.attachments() { - if let attachment = part as? MCOAttachment, let data = MailHandler.extractPGPSignature(attachment: attachment) { - sig = data - break - } - } - } - - return (MailHandler.extractSignedData(data: data), sig) - } - - private static func matches(for regex: String, in text: String) -> [String] { - do { - let regex = try NSRegularExpression(pattern: regex) - let results = regex.matches(in: text, - range: NSRange(text.startIndex..., in: text)) - return results.map { - String(text[Range($0.range, in: text)!]) - } - } catch { - return [] - } - } - private static func findBoundary(rawMail: String) -> String? { - let boundaries = matches(for: "boundary=.*;", in: rawMail) - if let bound = boundaries.first { - let splitted = bound.split(separator: "\"") - if splitted.count == 3 { - return "--"+String(splitted[1]) as String - } - } - return nil - } - - private static func extractSignedData(data: Data) -> Data? { - if let dataString = String.init(data: data, encoding: .utf8){ - if dataString.contains("Content-Type: multipart/signed") , let boundary = findBoundary(rawMail: dataString) { - var parts = dataString.components(separatedBy: boundary).dropFirst().filter({(s: String) -> Bool in - // We drop pgp signature. - // A messages, where a signed part is signed again, are strange and verification can fail -> What about a response etc? - if s.contains("-----BEGIN PGP SIGNATURE-----") { - return false - } - return true - }) - parts = parts.map({(s: String) -> String in - // Convert strings according to RFC3156. See: https://tools.ietf.org/html/rfc3156 page 6 - var newString = s.trimmed() - newString = newString.replacingOccurrences(of: "\r\n", with: "\n") - newString = newString.replacingOccurrences(of: "\n", with: "\r\n") - // Sometimes tabs got lost?! TEST TODO - newString = newString.replacingOccurrences(of: " charset", with: "\tcharset") - //TODO: Remove -- if last symbol - if newString.hasSuffix("--") { - newString = String(newString.dropLast(2)) - } - return newString - }) - if parts.count == 0 { - return parts.first?.data(using: .utf8) - } - else { - let res = parts.joined(separator: "\r\n") - return res.data(using: .utf8) - } - } - } - return nil - } - - - private static func extractPGPSignature(attachment: MCOAttachment) -> Data? { - if attachment.mimeType == "application/pgp-signature" { - return attachment.data - } - return nil - } - - - private func parseBody(msgParser: MCOMessageParser) -> String { - if let text = msgParser.plainTextBodyRenderingAndStripWhitespace(false) { - var body = text - var c = body.first - while (c != nil && CharacterSet.whitespacesAndNewlines.contains((c?.unicodeScalars.first)!)) { - body.removeFirst() - c = body.first - } - c = body.last - while (c != nil && CharacterSet.whitespacesAndNewlines.contains((c?.unicodeScalars.first)!)) { - body.removeLast() - c = body.last - } - return body - } - return "" - } - - private func findInlinePGP(text: String) -> String? { - var range = text.range(of: "-----BEGIN PGP MESSAGE-----") - if let lower = range?.lowerBound { - range = text.range(of: "-----END PGP MESSAGE-----") - if let upper = range?.upperBound { - let retValue = String(text[lower..<upper]) - // We do not try to decrypt a previous mails. - if retValue.contains(">"){ - return nil - } - return retValue - } - } - return nil - } - - - private func findKeyString(content: String) -> [String] { - var newKey = [String]() - if content.contains("-----BEGIN PGP PUBLIC KEY BLOCK-----") { - if let start = content.range(of: "-----BEGIN PGP PUBLIC KEY BLOCK-----") { - var end = content.range(of: "-----END PGP PUBLIC KEY BLOCK-----\n") - if end == nil { - end = content.range(of: "-----END PGP PUBLIC KEY BLOCK-----") - } - if let end = end { - let s = start.lowerBound - let e = end.upperBound - let pk = content[s..<e] - let pgp = SwiftPGP() - if let keyId = try? pgp.importKeys(key: String(pk), pw: nil, isSecretKey: false, autocrypt: false) { - newKey.append(contentsOf: keyId) - } - } - } - } - return newKey - } - - private func parsePublicKeys(attachment: MCOAttachment) -> [String] { - var newKey = [String]() - if let content = attachment.decodedString() { - newKey.append(contentsOf: findKeyString(content: content)) - } else if attachment.mimeType == "application/octet-stream", let content = String(data: attachment.data, encoding: String.Encoding.utf8), content.hasPrefix("-----BEGIN PGP PUBLIC KEY BLOCK-----") && (content.hasSuffix("-----END PGP PUBLIC KEY BLOCK-----") || content.hasSuffix("-----END PGP PUBLIC KEY BLOCK-----\n")) { - let pgp = SwiftPGP() - if let keyId = try? pgp.importKeys(key: content, pw: nil, isSecretKey: false, autocrypt: false) { - newKey.append(contentsOf: keyId) - } - } else if attachment.mimeType == "application/pgp-keys" { - let pgp = SwiftPGP() - if let keyIds = try? pgp.importKeys(data: attachment.data, pw: nil, secret: false) { - newKey.append(contentsOf: keyIds) - } - } - return newKey - } - private func parseSecretKey(attachment: MCOAttachment) -> String? { - if let content = attachment.decodedString() { - if content.contains("-----BEGIN PGP PRIVATE KEY BLOCK-----") { - if let start = content.range(of: "-----BEGIN PGP PRIVATE KEY BLOCK-----"), - let end = content.range(of: "-----END PGP PRIVATE KEY BLOCK-----") { - let s = start.lowerBound - let e = end.upperBound - let sk = String(content[s..<e]) - return sk - } - } - } - return nil - } - private func decryptText(body: String, from: MCOAddress?, autocrypt: Autocrypt?) -> CryptoObject? { - var sender: String? = nil - if let fromMCO = from { - sender = fromMCO.mailbox - } - if let data = body.data(using: String.Encoding.utf8, allowLossyConversion: true) as Data? { - let pgp = SwiftPGP() - var keyIds = [String]() - if sender != nil, let adr = DataHandler.handler.findMailAddress(adr: sender!) { - for k in adr.publicKeys { - keyIds.append(k.keyID) - } - } - if let a = autocrypt { - if let key = try? pgp.importKeys(key: a.key, pw: nil, isSecretKey: false, autocrypt: true) { - keyIds.append(contentsOf: key) - } - } - let secretkeys = DataHandler.handler.findSecretKeys() - var decIds = [String]() - for sk in secretkeys { - if let id = sk.keyID { - decIds.append(id) - } - } - - return pgp.decrypt(data: data, decryptionIDs: decIds, verifyIds: keyIds, fromAdr: sender) - } - - return nil - } - - func checkSMTP(_ completion: @escaping (MailServerConnectionError?) -> Void) { if AppDelegate.getAppDelegate().currentReachabilityStatus == .notReachable { completion(MailServerConnectionError.NoInternetconnection) diff --git a/enzevalos_iphone/SwiftPGP.swift b/enzevalos_iphone/SwiftPGP.swift index 428432c622766cc623a079c7b5c3c789a0cac136..9ed63affcd6f2b587a916c6f13ec52f11bf0fc48 100644 --- a/enzevalos_iphone/SwiftPGP.swift +++ b/enzevalos_iphone/SwiftPGP.swift @@ -238,6 +238,16 @@ class SwiftPGP: Encryption { } } + func importKeys(keys: [String]) -> [String] { + var keyIds = [String]() + for key in keys { + if let newIds = try? importKeys(key: key, pw: nil, isSecretKey: false, autocrypt: false) { + keyIds.append(contentsOf: newIds) + } + } + return keyIds + } + func importKeys (key: String, pw: String?, isSecretKey: Bool, autocrypt: Bool) throws -> [String]{ var keys = [Key]() if autocrypt{ @@ -387,7 +397,55 @@ class SwiftPGP: Encryption { } - func decrypt(data: Data, decryptionIDs: [String], verifyIds: [String], fromAdr: String?) -> CryptoObject{ + func verify(data: Data, attachedSignature: Data, verifyId: String, fromAdr: String) -> CryptoObject { + var sigKeyID: String? = nil + var signedAdr = [String]() + var sigState = SignatureState.NoSignature + let encState = EncryptionState.NoEncryption + let keyring = Keyring() + + if let key = loadKey(id: verifyId){ + keyring.import(keys: [key]) + } + + do{ + let keys = keyring.keys + try ObjectivePGP.verify(data, withSignature: attachedSignature, using: keys, passphraseForKey: loadPassword) + sigState = SignatureState.ValidSignature + sigKeyID = verifyId + signedAdr = vaildAddress(key: keys.first) + } catch { + let nsError = error as NSError + print(nsError) + switch nsError.code { + case 7: // no public key + sigState = SignatureState.NoPublicKey + case 8: // no signature + sigState = SignatureState.NoSignature + case 9: // unable to decrypt + sigState = SignatureState.InvalidSignature + default: + sigState = SignatureState.InvalidSignature + } + } + if !signedAdr.contains(fromAdr) && sigState == SignatureState.ValidSignature { + sigState = .InvalidSignature + } + + return CryptoObject(chiphertext: data, plaintext: nil, decryptedData: nil, sigState: sigState, encState: encState, signKey: sigKeyID, encType: .PGP, signedAdrs: signedAdr) + } + + func decrypt(mail: IncomingMail) -> CryptoObject { + let decIds = mail.decryptionKeyIDs + let data = mail.cryptoData + let sigIDs = mail.signatureKeyIDs + if let addr = mail.signatureAddr { + return decrypt(data: data, attachedSignature: nil, decKeyIDs: decIds, signatureIDs: sigIDs, fromAddr: addr) + } + return CryptoObject(chiphertext: data, plaintext: nil, decryptedData: nil, sigState: .InvalidSignature, encState: .UnableToDecrypt, signKey: nil, encType: .PGP, signedAdrs: []) //TODO: Check decryption only + } + + func decrypt(data: Data, attachedSignature: Data? = nil, decKeyIDs: [String], signatureIDs: [String], fromAddr: String) -> CryptoObject{ let prefKey = DataHandler.handler.prefSecretKey() var plaindata: Data? = nil var plaintext: String? = nil @@ -401,58 +459,59 @@ class SwiftPGP: Encryption { DECRYPTION */ // TODO: Maybe consider: try ObjectivePGP.recipientsKeyID(forMessage: ...) but currently not working... - for decID in decryptionIDs{ + for decID in decKeyIDs{ if let decKey = loadKey(id: decID){ if decID == prefID{ + keyring.import(keys: [decKey]) let (currentPlain, currentEncState) = decryptMessage(data: data, keys: [decKey], encForCurrentSK: true) if encState != EncryptionState.ValidEncryptedWithOldKey || currentEncState == EncryptionState.ValidedEncryptedWithCurrentKey{ plaindata = currentPlain encState = currentEncState + break } } - keyring.import(keys: [decKey]) } } if encState != EncryptionState.ValidedEncryptedWithCurrentKey{ + for decID in decKeyIDs { + if let decKey = loadKey(id: decID) { + keyring.import(keys: [decKey]) + } + } (plaindata, encState) = decryptMessage(data: data, keys: keyring.keys, encForCurrentSK: false) } /* VERIFICATION */ // test if message ist signed - sigState = verifyMessage(data: data, keys: keyring.keys) - - for id in verifyIds{ - if let key = loadKey(id: id){ - keyring.import(keys: [key]) - let currentState = verifyMessage(data: data, keys: keyring.keys) - if currentState == SignatureState.ValidSignature{ - sigState = currentState - sigKeyID = id - signedAdr = vaildAddress(key: key) - break - } - if currentState == SignatureState.InvalidSignature{ - sigState = currentState + let (currentSigState, signedID, signedAddr) = verifyData(toVerifyData: data, attachedSignature: attachedSignature, signatureIDs: signatureIDs, keyring: keyring) + print(currentSigState) + if currentSigState == .ValidSignature { + sigState = currentSigState + sigKeyID = signedID + signedAdr = signedAddr + } + else { + sigState = currentSigState + if let plainData = plaindata { + let (currentSigState2, signedID2, signedAddr2) = verifyData(toVerifyData: plainData, attachedSignature: attachedSignature, signatureIDs: signatureIDs, keyring: Keyring()) + if currentSigState2.rawValue > sigState.rawValue { + sigState = currentSigState2 + sigKeyID = signedID2 + signedAdr = signedAddr2 } } } if sigState == SignatureState.ValidSignature && sigKeyID == nil{ - for id in decryptionIDs{ - if let key = loadKey(id: id){ - keyring.import(keys: [key]) - let currentState = verifyMessage(data: data, keys: keyring.keys) - if currentState == SignatureState.ValidSignature{ - sigState = currentState - sigKeyID = id - signedAdr = vaildAddress(key: key) - break - } - if currentState == SignatureState.InvalidSignature{ - sigState = currentState - } - } + let currentState = verifyData(toVerifyData: data, attachedSignature: attachedSignature, signatureIDs: decKeyIDs, keyring: keyring) + if currentState.sigState == SignatureState.ValidSignature{ + sigState = currentState.sigState + sigKeyID = currentState.sigKeyID + signedAdr = currentState.signedAdr + } + if currentState.sigState == SignatureState.InvalidSignature{ + sigState = currentState.sigState } } @@ -465,18 +524,37 @@ class SwiftPGP: Encryption { else if encState == .NoEncryption{ plaintext = String(data: data, encoding: String.Encoding.utf8) } + + if sigState == .ValidSignature && !signedAdr.contains(fromAddr) { + sigState = .InvalidSignature + } return CryptoObject(chiphertext: data, plaintext: plaintext, decryptedData: plaindata, sigState: sigState, encState: encState, signKey: sigKeyID, encType: CryptoScheme.PGP, signedAdrs: signedAdr) } - - func decrypt(data: Data,decryptionId: String?, verifyIds: [String], fromAdr: String?) -> CryptoObject{ - if let decId = decryptionId{ - return decrypt(data: data, decryptionIDs: [decId], verifyIds: verifyIds, fromAdr: fromAdr) + private func verifyData(toVerifyData: Data, attachedSignature: Data?, signatureIDs: [String], keyring: Keyring) -> (sigState: SignatureState, sigKeyID: String?, signedAdr: [String]) { + var sigState = verifySignature(data: toVerifyData, attachedSignature: attachedSignature, keys: []) + print(sigState) + var sigKeyID: String? + var signedAdr: [String] = [] + for id in signatureIDs { + if let key = loadKey(id: id){ + keyring.import(keys: [key]) + let currentState = verifySignature(data: toVerifyData, attachedSignature: attachedSignature, keys: keyring.keys) + print(currentState) + if currentState == SignatureState.ValidSignature{ + sigState = currentState + sigKeyID = id + signedAdr = vaildAddress(key: key) + break + } + if currentState == SignatureState.InvalidSignature{ + sigState = currentState + } + } } - return decrypt(data: data, decryptionIDs: [String](), verifyIds: verifyIds, fromAdr: fromAdr) + return (sigState, sigKeyID, signedAdr) } - private func decryptMessage(data: Data, keys: [Key], encForCurrentSK: Bool) -> (Data?, EncryptionState){ if let dataString = String(data: data, encoding: .utf8) { do { @@ -506,28 +584,43 @@ class SwiftPGP: Encryption { return (nil, EncryptionState.NoEncryption) } - private func verifyMessage(data: Data, keys: [Key]) -> SignatureState{ - if let dataString = String(data: data, encoding: .utf8), let unarmored = try? Armor.readArmored(dataString){ - do{ - try ObjectivePGP.verify(unarmored, withSignature: nil, using: keys, passphraseForKey: loadPassword) - return SignatureState.ValidSignature - } catch { - let nsError = error as NSError - switch nsError.code { - case 7: // no public key - return SignatureState.NoPublicKey - case 8: // no signature - return SignatureState.NoSignature - case 9: // unable to decrypt - return SignatureState.InvalidSignature - default: - return SignatureState.InvalidSignature - } - } + private func verifySignature(sigString: String, attachedSignature: Data?, keys: [Key]) -> SignatureState { + if let unarmored = try? Armor.readArmored(sigString){ + return verifySignature(data: unarmored, attachedSignature: attachedSignature, keys: keys) + } + if let data = sigString.data(using: .utf8) { + return verifySignature(data: data, attachedSignature: attachedSignature, keys: keys) } return SignatureState.NoSignature } + private func verifySignature(data: Data, attachedSignature: Data?, keys: [Key]) -> SignatureState { + var sigData = data + var sigState = SignatureState.NoSignature + if let dataString = String(data: data, encoding: .utf8), let unarmored = try? Armor.readArmored(dataString){ + sigData = unarmored + } + do{ + if keys.isEmpty && attachedSignature != nil { + return SignatureState.NoPublicKey + } + try ObjectivePGP.verify(sigData, withSignature: attachedSignature, using: keys, passphraseForKey: loadPassword) + sigState = SignatureState.ValidSignature + } catch { + let nsError = error as NSError + switch nsError.code { + case 7: // no public key + sigState = SignatureState.NoPublicKey + case 8: // no signature + sigState = SignatureState.NoSignature + case 9: // unable to decrypt + sigState = SignatureState.InvalidSignature + default: + sigState = SignatureState.InvalidSignature + } + } + return sigState + } func vaildAddress(key: Key?) -> [String]{ var adrs = [String]() diff --git a/enzevalos_iphone/mail/IncomingMail.swift b/enzevalos_iphone/mail/IncomingMail.swift new file mode 100644 index 0000000000000000000000000000000000000000..3b98565c6004ee88fa1ec8bde6f94585f7e4f3f8 --- /dev/null +++ b/enzevalos_iphone/mail/IncomingMail.swift @@ -0,0 +1,596 @@ +// +// IncomingMail.swift +// enzevalos_iphone +// +// Created by Oliver Wiese on 06.03.19. +// Copyright © 2019 fu-berlin. All rights reserved. +// + +import Foundation + +/** + We like to parse and store a mail. + We like to: + find public keys, secret keys + decrypt, decrypt and verify or verify a mail + */ + +enum PGPPart { + case MESSAGE, PUBLICKEY, SIGNATURE, SECRETKEY; + + var start: String { + get { + return pgpBlock(start: true) + } + } + + var end: String { + get { + return pgpBlock(start: false) + } + } + + private func pgpBlock(start: Bool) -> String{ + var type = "END" + if start { + type = "BEGIN" + } + switch self { + case .MESSAGE: + return "-----\(type) PGP MESSAGE-----" + case .PUBLICKEY: + return "-----\(type) PGP PUBLIC KEY BLOCK-----" + case .SIGNATURE: + return "-----\(type) PGP SIGNATURE-----" + case .SECRETKEY: + return "-----\(type) PGP SECRET KEY BLOCK-----" + } + } + + func findPGPPartInString(content: String) -> [String]{ + var parts = [String]() + let startString = self.start + let endString = self.end + if content.contains(startString) { + if let start = content.range(of: startString) { + var end = content.range(of: endString+"\n") + if end == nil { + end = content.range(of: endString) + } + if let end = end { + let s = start.lowerBound + let e = end.upperBound + let part = content[s..<e] + // We do not consider parts of previous mails. + if !part.contains(">"){ + parts.append(String(part)) + } + } + } + } + return parts + } +} + +enum PgpMIMEType { + case SIGNATURE, ENCRYPTED, OCTET, KEYS; + + static let allValues = [PgpMIMEType.ENCRYPTED, PgpMIMEType.KEYS, PgpMIMEType.OCTET, PgpMIMEType.SIGNATURE] + + var name: String { + get{ + switch self { + case .SIGNATURE: + return "application/pgp-signature" + case .ENCRYPTED: + return "application/pgp-encrypted" + case .OCTET: + return "application/octet-stream" + case .KEYS: + return "application/pgp-keys" + } + } + } + + func isTypeOf(attachment: MCOAttachment) -> Bool { + return attachment.mimeType == self.name + } + + static func findType(attachment: MCOAttachment) -> PgpMIMEType? { + return findType(name: attachment.mimeType) + } + + static func findType(name: String) -> PgpMIMEType? { + for type in PgpMIMEType.allValues { + if type.name == name { + return type + } + } + return nil + } +} + +class IncomingMail { + private let rawData: Data + private var msgParser: MCOMessageParser + private let firstParser: MCOMessageParser + // Header fields + private let flags: MCOMessageFlag + private let uID: UInt64 + private let folderPath: String + + private var rec: [MCOAddress] = [] + private var cc: [MCOAddress] = [] + private var from: MCOAddress? + private var fromKeyIds: [String] { + get{ + var keyIds: [String] = [] + keyIds.append(contentsOf: newAutocrypPublicKeys) + keyIds.append(contentsOf: newPublicKeys) + if let fromAdr = from?.mailbox{ + if let adr = DataHandler.handler.findMailAddress(adr: fromAdr) { + for k in adr.publicKeys { + keyIds.append(k.keyID) + } + } + } + return keyIds + } + } + private var subject: String = "" + private var date: Date = Date() + private var autocrypt: Autocrypt? = nil + private var references: [String] = [] + private var boundary: String? + private var msgID: String = "" + private var userAgent: String = "" + + + // Body info + private var secretKey: String? = nil + private var isEnc = false + private var html: String = "" + private var lineArray: [String] = [] + private var cryptoObj: CryptoObject? = nil + private var body: String = "" + private var secretKeys: [String] = [] + private var newPublicKeys: [String] = [] + private var newAutocrypPublicKeys: [String] = [] + + + // Crypto info + var cryptoData: Data { + get { + if let data = body.data(using: String.Encoding.utf8, allowLossyConversion: true) { + return data + } + return msgParser.data() + } + } + var decryptionKeyIDs: [String] { + get { + let secretkeys = DataHandler.handler.findSecretKeys() + var decIds = [String]() + for sk in secretkeys { + if let id = sk.keyID { + decIds.append(id) + } + } + return decIds + } + } + + var primaryDecryptionKeyID: String? { + let sk = DataHandler.handler.prefSecretKey() + return sk.keyID + } + + var signatureKeyIDs: [String] { + get { + return fromKeyIds + } + } + + var signatureAddr: String? { + get { + if let adr = from?.mailbox { + return adr + } + return nil + } + } + + + init(rawData: Data, uID: UInt64, folderPath: String, flags: MCOMessageFlag){ + self.rawData = rawData + self.uID = uID + self.folderPath = folderPath + self.flags = flags + self.firstParser = MCOMessageParser(data: rawData) + self.msgParser = firstParser + self.parseHeader() + self.parseBody() + } + + func store(keyRecord: KeyRecord?) -> PersistentMail? { + let sk = secretKeys.first //TODO FIX + let mail = DataHandler.handler.createMail(uID, sender: from, receivers: rec, cc: cc, time: date, received: true, subject: subject, body: body, flags: flags, record: keyRecord, autocrypt: autocrypt, decryptedData: cryptoObj, folderPath: folderPath, secretKey: sk) + if let m = mail { + let pgp = SwiftPGP() + if let autoc = autocrypt, let adr = from?.mailbox { + if let publickeys = try? pgp.importKeys(key: autoc.key, pw: nil, isSecretKey: false, autocrypt: true) { + for pk in publickeys { + _ = DataHandler.handler.newPublicKey(keyID: pk, cryptoType: CryptoScheme.PGP, adr: adr, autocrypt: true, firstMail: mail) + } + } + } + if let adr = from?.mailbox { + for keyId in newPublicKeys { + _ = DataHandler.handler.newPublicKey(keyID: keyId, cryptoType: CryptoScheme.PGP, adr: adr, autocrypt: false, firstMail: mail) + } + } + Logger.log(received: m) + } + return mail + } + + + + + + private func parseBody() { + var isEncrypted = false + var isSigned = false + var encData: Data? + var publicKeyRaw: [String] = [] + var secretKeyRaw: [String] = [] + var signaturesRaw: [String] = [] + let pgp = SwiftPGP() + for a in msgParser.attachments() { + if let attachment = a as? MCOAttachment { + if let pgpType = PgpMIMEType.findType(attachment: attachment) { + if pgpType == PgpMIMEType.ENCRYPTED { + isEncrypted = true + } + else if pgpType == PgpMIMEType.OCTET { + encData = attachment.data + } + else if pgpType == PgpMIMEType.SIGNATURE { + isSigned = true + signaturesRaw.append(contentsOf: IncomingMail.extractPGPSignature(attachment: attachment)) + } + else { + publicKeyRaw.append(contentsOf: IncomingMail.findPublicKeys(attachment: attachment)) + secretKeyRaw.append(contentsOf: IncomingMail.findSecretKeys(attachment: attachment)) + } + } + } + } + body = msgParser.plainTextBodyRenderingAndStripWhitespace(false) + publicKeyRaw.append(contentsOf:IncomingMail.extractPublicKeys(text: body)) + secretKeyRaw.append(contentsOf:IncomingMail.extractSecretKeys(text: body)) + if let encData = encData, isEncrypted { + // We drop plaintext keys. + publicKeyRaw = [] + secretKeyRaw = [] + msgParser = MCOMessageParser(data: encData) + prepareDecryption() + let cryptoObject = pgp.decrypt(mail: self) + mergeCryptoObject(newCryptoObj: cryptoObject) + msgParser = MCOMessageParser(data: cryptoObject.decryptedData) + return parseBody() + } + else if isSigned { + + let signedStrings = extractSignedParts(data: msgParser.data()) + //Signatur prüfen und dann mit Rest neu parsen??? Was wenn plainText? + var text: String? + var signedObject: CryptoObject? + if signedStrings.count == 1 { + text = signedStrings.first + } + else if signedStrings.count > 1 { + text = signedStrings.joined(separator: "\r\n") + } + if let signedData = text?.data(using: .utf8){ + for sig in signaturesRaw { + if let signature = sig.data(using: .utf8), let adr = from?.mailbox { + for id in fromKeyIds { + signedObject = pgp.verify(data: signedData, attachedSignature: signature, verifyId: id, fromAdr: adr) + if let ob = signedObject, ob.signatureState == .ValidSignature { + break + } + } + if let signedObject = signedObject { + mergeCryptoObject(newCryptoObj: signedObject) + if signedObject.signatureState != .NoSignature { + msgParser = MCOMessageParser(data: signedData) + return parseBody() + } + } + } + } + } + } + else { + plainTextBody() + let chiphers = IncomingMail.extractInlinePGP(text: body) + // We consider only the first encrypted part! TODO: improve + if let text = chiphers.first, let data = text.data(using: .utf8) { + msgParser = MCOMessageParser(data: data) + let cryptoObject = pgp.decrypt(mail: self) + mergeCryptoObject(newCryptoObj: cryptoObject) + msgParser = MCOMessageParser(data: cryptoObject.decryptedData) + return parseBody() + + } + } + newPublicKeys = pgp.importKeys(keys: publicKeyRaw) + for sk in secretKeyRaw { + if let sks = try? pgp.importKeys(key: sk, pw: nil, isSecretKey: true, autocrypt: false){ + secretKeys.append(contentsOf: sks) + } + } + } + + + private func mergeCryptoObject(newCryptoObj: CryptoObject) { + if let oldCrypto = cryptoObj { + var sigState = oldCrypto.signatureState + var encState = oldCrypto.encryptionState + var signKey = oldCrypto.signKey + var signedAddr = oldCrypto.signedAdrs + + if newCryptoObj.signatureState.rawValue > sigState.rawValue { + // TODO: What about dropped information? Users are confused? + sigState = newCryptoObj.signatureState + signKey = newCryptoObj.signKey + signedAddr = newCryptoObj.signedAdrs + } + if newCryptoObj.encryptionState.rawValue > encState.rawValue { + encState = newCryptoObj.encryptionState + } + cryptoObj = CryptoObject(chiphertext: oldCrypto.chiphertext, plaintext: newCryptoObj.plaintext, decryptedData: newCryptoObj.decryptedData, sigState: sigState, encState: encState, signKey: signKey, encType: newCryptoObj.encType, signedAdrs: signedAddr) + } + else { + cryptoObj = newCryptoObj + } + + } + + private func prepareDecryption() { + html = msgParser.plainTextRendering() + lineArray = html.components(separatedBy: "\n") + lineArray.removeFirst(4) + body = lineArray.joined(separator: "\n") + body = body.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) + body.append("\n") + } + + private func plainTextBody() { + if let text = msgParser.plainTextBodyRenderingAndStripWhitespace(false) { + body = text + var c = body.first + while (c != nil && CharacterSet.whitespacesAndNewlines.contains((c?.unicodeScalars.first)!)) { + body.removeFirst() + c = body.first + } + c = body.last + while (c != nil && CharacterSet.whitespacesAndNewlines.contains((c?.unicodeScalars.first)!)) { + body.removeLast() + c = body.last + } + } + } + + private func parseHeader() { + if let header = msgParser.header { + if let refs = header.references { + for ref in refs{ + if let string = ref as? String { + references.append(string) + } + } + } + if let tos = header.to { + for r in tos{ + rec.append(r as! MCOAddress) + } + } + if let ccs = header.cc { + for r in ccs { + cc.append(r as! MCOAddress) + } + } + + if let _ = header.extraHeaderValue(forName: Autocrypt.AUTOCRYPTHEADER){ + let autocrypt = Autocrypt(header: header) + self.autocrypt = autocrypt + let pgp = SwiftPGP() + let keys = try? pgp.importKeys(key: autocrypt.key, pw: nil, isSecretKey: false, autocrypt: true) + if let keys = keys { + newAutocrypPublicKeys = keys + } + } + if let _ = header.extraHeaderValue(forName: Autocrypt.SETUPMESSAGE) { + // TODO: Distinguish between other keys (future work) + } + if let f = header.from { + from = f + } + if let sub = header.subject { + subject = sub + } + if let d = header.date { + date = d + } + if let id = header.messageID { + msgID = id + } + if let agent = header.userAgent { + userAgent = agent + } + boundary = findBoundary() + } + } + + + private func extractSignedParts(data: Data) -> [String] { + if let text = String(data: data, encoding: .utf8) { + return extractSignedParts(text: text) + } + return [String] () + } + + private func extractSignedParts(text: String) -> [String] { + var parts: [String] = [] + var boundary = IncomingMail.findBoundary(text: text) + if boundary == nil { + boundary = self.boundary + } + guard text.contains("Content-Type: multipart/signed") && boundary != nil else { + return parts + } + + parts = text.components(separatedBy: boundary!) + // If text contains a header -> we drop it + if let header = parts.first, IncomingMail.findBoundary(text: header) != nil { + parts = Array(parts.dropFirst()) + } + parts = Array(parts.filter({(s: String ) -> Bool in + // A messages, where a signed part is signed again, are strange and verification can fail -> What about a response etc? + if s.contains(PGPPart.SIGNATURE.start) { + return false + } + return true + })) + + parts = Array(parts.map({(s: String) -> String in + // Convert strings according to RFC3156. See: https://tools.ietf.org/html/rfc3156 page 6 + var newString = s.replacingOccurrences(of: "\r\n", with: "\n") + newString = newString.replacingOccurrences(of: "\n", with: "\r\n") + // Sometimes we lose tabs... TEST TODO + newString = newString.replacingOccurrences(of: " charset", with: "\tcharset") + //TODO: Remove -- if last symbol + if newString.hasSuffix("--") { + newString = String(newString.dropLast(2)) + } + else if newString.hasSuffix("--\r\n") { + newString = String(newString.dropLast(4)) + } + if newString.hasPrefix("\r\n") { + newString = String(newString.dropFirst()) + } + if newString.hasSuffix("\r\n") { + newString = String(newString.dropLast()) + } + newString = newString.trimmingCharacters(in: .whitespaces) + return newString + })) + + parts = Array(parts.filter({(s: String ) -> Bool in + if s.isEmpty { + return false + } + return true + })) + return parts + } + + + + private func findBoundary() -> String? { + if let rawString = String.init(data: rawData, encoding:.utf8) { + return IncomingMail.findBoundary(text: rawString) + } + return nil + } + + func findSignedDataAndSignature(data: Data) -> (signedTexts: [String], signature: [String]) { + var sig: [String] = [] + if let parser = MCOMessageParser.init(data: data) { + for part in parser.attachments() { + if let attachment = part as? MCOAttachment { + let data = IncomingMail.extractPGPSignature(attachment: attachment) + sig.append(contentsOf: data) + // TODO: What about more signatures? + } + } + } + + return (extractSignedParts(data: data), sig) + } + + private static func importPublicKeys(attachment: MCOAttachment) -> [String] { + var newKey = [String]() + let pgp = SwiftPGP() + if let content = attachment.decodedString() { + newKey = pgp.importKeys(keys: PGPPart.PUBLICKEY.findPGPPartInString(content: content)) + } + else if PgpMIMEType.KEYS.isTypeOf(attachment: attachment) { + if let keyIds = try? pgp.importKeys(data: attachment.data, pw: nil, secret: false) { + newKey.append(contentsOf: keyIds) + } + } + return newKey + } + + private static func findSecretKeys(attachment: MCOAttachment) -> [String] { + if let content = attachment.decodedString() { + return PGPPart.SECRETKEY.findPGPPartInString(content: content) + } + return [] + } + + private static func findPublicKeys(attachment: MCOAttachment) -> [String] { + if let content = attachment.decodedString() { + return PGPPart.PUBLICKEY.findPGPPartInString(content: content) + } + return [] + } + + private static func extractPublicKeys(text: String) -> [String] { + return PGPPart.PUBLICKEY.findPGPPartInString(content: text) + + } + + private static func extractSecretKeys(text: String) -> [String] { + return PGPPart.SECRETKEY.findPGPPartInString(content: text) + + } + + private static func extractInlinePGP(text: String) -> [String] { + return PGPPart.MESSAGE.findPGPPartInString(content: text) + } + + private static func extractPGPSignature(attachment: MCOAttachment) -> [String] { + if let content = String(data: attachment.data, encoding: .utf8) { + return PGPPart.SIGNATURE.findPGPPartInString(content: content) + } + return [] + } + + + + private static func findBoundary(text: String) -> String? { + let boundaries = matches(for: "boundary=.*;", in: text) + if let bound = boundaries.first { + let splitted = bound.split(separator: "\"") + if splitted.count == 3 { + return "--"+String(splitted[1]) as String + } + } + return nil + } + private static func matches(for regex: String, in text: String) -> [String] { + do { + let regex = try NSRegularExpression(pattern: regex) + let results = regex.matches(in: text, range: NSRange(text.startIndex..., in: text)) + return results.map { + String(text[Range($0.range, in: text)!]) + } + } catch { + return [] + } + } +} diff --git a/enzevalos_iphoneTests/CoreDataTests.swift b/enzevalos_iphoneTests/CoreDataTests.swift index 0f46754434141bf5bf8be71a83c41500d23fc5d4..9708565308addd02ee762069c0619a22d1137aac 100644 --- a/enzevalos_iphoneTests/CoreDataTests.swift +++ b/enzevalos_iphoneTests/CoreDataTests.swift @@ -264,12 +264,11 @@ class CoraDataTests: XCTestCase { XCTAssertTrue(myrecord.isSecure) XCTAssertEqual(myContact.publicKeys.count, 2) let cryptoObject2 = pgp.encrypt(plaintext: body, ids: [newKeyId], myId: keyID) - let decryptObject2 = pgp.decrypt(data: cryptoObject2.chiphertext!, decryptionIDs: [newKeyId], verifyIds: [keyID], fromAdr: sender.mailbox) - + let decryptObject2 = pgp.decrypt(data: cryptoObject2.chiphertext!, decKeyIDs: [newKeyId], signatureIDs: [keyID], fromAddr: userAdr) _ = testMail(from: sender, to: [user], cc: [], bcc: [], folder: folderName, cryptoObject: decryptObject2) let cryptoObject3 = pgp.encrypt(plaintext: body, ids: [oldID], myId: keyID) - let decryptObject3 = pgp.decrypt(data: cryptoObject3.chiphertext!, decryptionIDs: [oldID], verifyIds: [keyID], fromAdr: sender.mailbox) + let decryptObject3 = pgp.decrypt(data: cryptoObject3.chiphertext!, decKeyIDs: [oldID], signatureIDs: [keyID], fromAddr: userAdr) let oldMail = testMail(from: sender, to: [user], cc: [], bcc: [], folder: folderName, cryptoObject: decryptObject3) XCTAssertTrue((oldMail?.decryptedWithOldPrivateKey)!) diff --git a/enzevalos_iphoneTests/CryptoTests.swift b/enzevalos_iphoneTests/CryptoTests.swift index 69f8ee17a151947ad2d4fc3e3613d42029bf0803..d8eabec7d52295dd2017647e74e7bc1f24376b97 100644 --- a/enzevalos_iphoneTests/CryptoTests.swift +++ b/enzevalos_iphoneTests/CryptoTests.swift @@ -360,17 +360,15 @@ class CryptoTests: XCTestCase { let signedMessage = """ -----BEGIN PGP SIGNATURE----- - iQGzBAEBCgAdFiEEJRxNIIPfYdSrprTDTbvambTX/SgFAlq5A9oACgkQTbvambTX - /SgIWwwAm1BTBjme8ogPdKcj2fyAzLjWPwttIW3nJN/tlB79TcId2sAAZK0hyhJQ - PU+j72x0IrgnJ0Vf40B9QU07RgLtdJPXXN1GWQPaz/69Wiut4T6mUusiqfE4RLiy - HJF7OYUNY6adteO1jCm0ZiZl3VW6XVomaXNuZtqXE+4U2k4WyQvaGbYMNvaaAYRf - vwYLyj/CO6++7lhYDet+A4OE5w0WcRHqrM4IRs/wF62cgBVl9cnonvl/2MTgR6DX - ewEcAeF+BmtMfVZhjEUa/zslLRTCRaxpVCq6BjHydakCTpZCJJTQcm+gVZrtQjl0 - 953zPJUfJ1/pvUextyKihT9a5itsxV2Tboq0mwD09M+hqWlHy0lQujI/hggcVvxE - DudoBwBZ4TilZyfRaSb9no1lJjxTZhwtsAvwOSPhRs3pGxkL9TS4GmqSsl8vZUBt - LBNFaFMvJfDXqmwIcW1Yl+apk7bWqY7yfTCgdRzhcqFDxdqhchNYvq4x035z4Udz - ZMarcccr - =OmRP + wsDcBAABCgAGBQJcgVq4AAoJEE272pm01/0o2Y0L/jS3UhX5ypJj1SKMVKWxHVW8CkzlHHsEtuhK + kkZs7sq9X8pqVGGeLjf+Bvz7dg5epAd76yD7M/hpHpEzAY+j4q0IjAQfkUApJIHnX303g9Xg2qC0 + KymaoHV2iD7TfkGdCpoP4lmq9DVwQWOs6ixKThO9QjNJFjZRFfI7bQlH2dKg89JVpLfoK438fm3t + pNAmcf60wYCc2SI4zGaduyiLBIHR9XG4tb9Tc82Sf3tguBNHpmTvmiJFCL7ZZNWg8lQP2kDmPkds + Ugo602abhT64VBxi4YPXHw2tNApEtniMbn03X1Z/oPMNDN3m+/0YZU6+Fc0RwJ9SBHvGxLSXH9qc + PH1tdx4Bm1z1P+oQrD9vRbTqxLM6g5vk0bG/5Esa4f1/yQWto5LH0cZXVG2Ugy2x+4fYWCumMXDS + Vj+6SjTsZpctBji7OUYA2vMNXRdtOBCrYvEoq+Nm2eATp0zba+2kUVYY5HJqDMxepdBUKI+esXDZ + wnNopEkrmkzNWaQZou1QtQ== + =p/QU -----END PGP SIGNATURE----- """ @@ -485,7 +483,7 @@ class CryptoTests: XCTestCase { XCTFail("No data") return } - let cryptoObject = pgp.decrypt(data: data, decryptionId: userKeyID, verifyIds: [], fromAdr: nil) + let cryptoObject = pgp.decrypt(data: data, decKeyIDs: [userKeyID], signatureIDs: [], fromAddr: userAdr) XCTAssert(cryptoObject.encryptionState == .NoEncryption) XCTAssert(cryptoObject.signatureState == .NoSignature) XCTAssert(cryptoObject.decryptedData == nil && cryptoObject.decryptedText == nil && cryptoObject.signKey == nil) @@ -502,7 +500,7 @@ class CryptoTests: XCTestCase { XCTFail("No chipher data") return } - let cryptoObject = pgp.decrypt(data: data, decryptionId: userKeyID, verifyIds: [], fromAdr: nil) + let cryptoObject = pgp.decrypt(data: data, attachedSignature: nil, decKeyIDs: [userKeyID], signatureIDs: [], fromAddr: userAdr) XCTAssert(cryptoObject.encryptionState == .ValidedEncryptedWithCurrentKey) XCTAssert(cryptoObject.signatureState == .NoSignature) XCTAssert(cryptoObject.plaintext == body && cryptoObject.plaintext == cryptoObject.decryptedText) @@ -520,13 +518,18 @@ class CryptoTests: XCTestCase { } // 1. case: correct signed mail - var cryptoObject = pgp.decrypt(data: signedData, decryptionId: keys.first, verifyIds: keys, fromAdr: nil) - XCTAssert(cryptoObject.encryptionState == .NoEncryption && cryptoObject.signatureState == .ValidSignature) - XCTAssert(cryptoObject.decryptedText == "only a signed mail!") - - // 2. case: manipulated mail - cryptoObject = pgp.decrypt(data: manipulatedDate, decryptionId: keys.first, verifyIds: keys, fromAdr: nil) - XCTAssert(cryptoObject.encryptionState == .NoEncryption && cryptoObject.signatureState == .InvalidSignature) + let text = "only a signed mail!" + if let data = text.data(using: .utf8) { + var cryptoObject = pgp.decrypt(data: data, attachedSignature: signedData, decKeyIDs: [userKeyID], signatureIDs: keys, fromAddr: "alice@enzevalos.de") + XCTAssert(cryptoObject.encryptionState == .NoEncryption && cryptoObject.signatureState == .ValidSignature) + XCTAssert(cryptoObject.chiperString == text) + // 2. case: manipulated mail + cryptoObject = pgp.decrypt(data: data, attachedSignature: manipulatedDate, decKeyIDs: keys, signatureIDs: keys, fromAddr: "alice@enzevalos.de") + XCTAssert(cryptoObject.encryptionState == .NoEncryption && cryptoObject.signatureState == .InvalidSignature) + } + else { + XCTFail("Can not make String to data.") + } } func testEncSignedMail() { @@ -537,30 +540,29 @@ class CryptoTests: XCTestCase { let senderPGP = SwiftPGP() let encObject = senderPGP.encrypt(plaintext: body, ids: [userKeyID], myId: senderID) XCTAssert(encObject.encryptionState == .ValidedEncryptedWithCurrentKey && encObject.signatureState == SignatureState.ValidSignature) - let falseEncObject = senderPGP.encrypt(plaintext: body, ids: [], myId: senderID) + let falseEncObject = senderPGP.encrypt(plaintext: body, ids: [id2], myId: senderID) guard let data = encObject.chiphertext, let data2 = falseEncObject.chiphertext else { XCTFail("no chipher data") return } // 1. case: signed but no public key available to verify signature - var cryptoObject = pgp.decrypt(data: data, decryptionId: userKeyID, verifyIds: [], fromAdr: nil) + var cryptoObject = pgp.decrypt(data: data, attachedSignature: nil, decKeyIDs: [userKeyID], signatureIDs: [], fromAddr: userAdr) XCTAssert(cryptoObject.encryptionState == .ValidedEncryptedWithCurrentKey) - XCTAssert(cryptoObject.signatureState == .NoPublicKey) + XCTAssert(cryptoObject.signatureState != .ValidSignature) // No PK XCTAssert(cryptoObject.plaintext == body && cryptoObject.plaintext == cryptoObject.decryptedText) - // 2. case: signed and public key available - cryptoObject = pgp.decrypt(data: data, decryptionId: userKeyID, verifyIds: [senderID, id2], fromAdr: nil) + cryptoObject = pgp.decrypt(data: data, attachedSignature: nil, decKeyIDs: [userKeyID], signatureIDs: [senderID, id2], fromAddr: senderAddress.mailbox) XCTAssert(cryptoObject.signatureState == .ValidSignature) XCTAssert(cryptoObject.signKey == senderID) XCTAssert(cryptoObject.signedAdrs.contains(senderAddress.mailbox) && cryptoObject.signedAdrs.count == 1) // 3. case: signed and check with wrong key - cryptoObject = pgp.decrypt(data: data, decryptionId: userKeyID, verifyIds: [id2], fromAdr: nil) - XCTAssert(cryptoObject.signatureState == .NoPublicKey) + cryptoObject = pgp.decrypt(data: data, decKeyIDs: [userKeyID], signatureIDs: [id2], fromAddr: senderAddress.mailbox) + XCTAssert(cryptoObject.signatureState != .ValidSignature) // 4. case: can not decrypt (wrong/missing decryption/encryption key) - cryptoObject = pgp.decrypt(data: data2, decryptionId: userKeyID, verifyIds: [senderID], fromAdr: nil) + cryptoObject = pgp.decrypt(data: data2, decKeyIDs: [userKeyID], signatureIDs: [senderID], fromAddr: senderAddress.mailbox) XCTAssert(cryptoObject.encryptionState == .UnableToDecrypt && cryptoObject.signatureState == .NoSignature) // 5. case: used old key to encrypt message @@ -572,7 +574,7 @@ class CryptoTests: XCTestCase { XCTAssertEqual(keys.count, 1) _ = datahandler.newSecretKeys(keyIds: keys, addPKs: true) XCTAssertEqual(keys.first, datahandler.prefSecretKey().keyID) - cryptoObject = pgp.decrypt(data: data, decryptionId: userKeyID, verifyIds: [senderID], fromAdr: nil) + cryptoObject = pgp.decrypt(data: data, decKeyIDs: [userKeyID], signatureIDs: [senderID], fromAddr: senderAddress.mailbox) XCTAssertEqual(keys.first, datahandler.prefSecretKey().keyID) XCTAssert(cryptoObject.encryptionState == .ValidEncryptedWithOldKey && cryptoObject.signatureState == .ValidSignature) XCTAssert(cryptoObject.decryptedText == body) diff --git a/enzevalos_iphoneTests/MailTest.swift b/enzevalos_iphoneTests/MailTest.swift index 1a48d4a3b30bb1bc6b4fa993c811dec2fbae5b6d..3b4f158b035b3a8c2000b48e107ee0e7d06fff70 100644 --- a/enzevalos_iphoneTests/MailTest.swift +++ b/enzevalos_iphoneTests/MailTest.swift @@ -82,9 +82,10 @@ class MailTest: XCTestCase { let subject = "subject" let body = "This is the body" let outMail = OutgoingMail(toEntrys: tos, ccEntrys: ccs, bccEntrys: bccs, subject: subject, textContent: body, htmlContent: nil) - if let parser = MCOMessageParser(data: outMail.plainData) { + if let data = outMail.plainData { // Test parsing! - if let mail = mailHandler.parseMail(parser: parser, record: nil, folderPath: "INBOX", uid: 0, flags: MCOMessageFlag.seen){ + let incMail = IncomingMail(rawData: data, uID: 0, folderPath: "INBOX", flags: MCOMessageFlag.init(rawValue: 0)) + if let mail = incMail.store(keyRecord: nil){ XCTAssertTrue(MailTest.compareAdrs(adrs1: tos, adrs2: mail.getReceivers())) XCTAssertTrue(MailTest.compareAdrs(adrs1: ccs, adrs2: mail.getCCs())) XCTAssertTrue(mail.getBCCs().count == 0) @@ -120,9 +121,10 @@ class MailTest: XCTestCase { https certs will be valid – Microsoft signed. """ let outMail = OutgoingMail(toEntrys: tos, ccEntrys: ccs, bccEntrys: bccs, subject: subject, textContent: body, htmlContent: nil) - if let parser = MCOMessageParser(data: outMail.plainData) { + if let data = outMail.plainData { // Test parsing! - if let mail = mailHandler.parseMail(parser: parser, record: nil, folderPath: "INBOX", uid: 0, flags: MCOMessageFlag.seen){ + let incMail = IncomingMail(rawData: data, uID: 0, folderPath: "INBOX", flags: MCOMessageFlag.init(rawValue: 0)) + if let mail = incMail.store(keyRecord: nil){ XCTAssertEqual(subject, mail.subject) XCTAssertEqual(body, mail.body) } @@ -143,8 +145,9 @@ class MailTest: XCTestCase { let body = "body" _ = createPGPUser(adr: encAdr, name: encAdr) let outMail = OutgoingMail(toEntrys: [encAdr], ccEntrys: [], bccEntrys: [], subject: subject, textContent: body, htmlContent: nil) - if let parser = MCOMessageParser(data: outMail.pgpData){ - if let mail = mailHandler.parseMail(parser: parser, record: nil, folderPath: "INBOX", uid: 1, flags: MCOMessageFlag.seen) { + if let data = outMail.pgpData{ + let incMail = IncomingMail(rawData: data, uID: 1, folderPath: "INBOX", flags: MCOMessageFlag.init(rawValue: 0)) + if let mail = incMail.store(keyRecord: nil) { XCTAssertEqual(body, mail.body) XCTAssertTrue(mail.isSecure) } @@ -164,8 +167,9 @@ class MailTest: XCTestCase { let body = "body" _ = createPGPUser(adr: encAdr, name: encAdr) let outMail = OutgoingMail(toEntrys: [plainAdr, encAdr], ccEntrys: [], bccEntrys: [], subject: subject, textContent: body, htmlContent: nil) - if let secureParser = MCOMessageParser(data: outMail.pgpData) { - if let mail = mailHandler.parseMail(parser: secureParser, record: nil, folderPath: "INBOX", uid: 2, flags: MCOMessageFlag.seen) { + if let data = outMail.pgpData { + let incMail = IncomingMail(rawData: data, uID: 2, folderPath: "INBOX", flags: MCOMessageFlag.init(rawValue: 0)) + if let mail = incMail.store(keyRecord: nil){ XCTAssertEqual(body, mail.body) XCTAssertTrue(mail.isSecure) XCTAssertTrue(MailTest.compareAdrs(adrs1: [encAdr, plainAdr], adrs2: mail.getReceivers())) @@ -177,8 +181,10 @@ class MailTest: XCTestCase { else { XCTFail() } - if let insecureParser = MCOMessageParser(data: outMail.plainData) { - if let mail = mailHandler.parseMail(parser: insecureParser, record: nil, folderPath: "INXBO", uid: 3, flags: MCOMessageFlag.seen) { + if let data = outMail.plainData { + let incMail = IncomingMail(rawData: data, uID: 3, folderPath: "INBOX", flags: MCOMessageFlag.init(rawValue: 0)) + + if let mail = incMail.store(keyRecord: nil) { XCTAssertEqual(body, mail.body) XCTAssertFalse(mail.isSecure) XCTAssertTrue(MailTest.compareAdrs(adrs1: [plainAdr, encAdr], adrs2: mail.getReceivers())) @@ -232,23 +238,22 @@ class MailTest: XCTestCase { func testMailAliceToBob(pkExists: Bool, name: String, isSecure: Bool, encState: EncryptionState? = nil, sigState: SignatureState? = nil) { let mailData = MailTest.loadMail(name: name ) let (alice, _) = addAliceAndBob(addAlice: pkExists) - if let parser = MCOMessageParser(data: mailData) { - if let mail = mailHandler.parseMail(parser: parser, record: nil, folderPath: "INBOX", uid: 0, flags: MCOMessageFlag.seen) { - XCTAssertEqual(mail.isSecure, isSecure) - if mail.isSecure || mail.sigState == .ValidSignature{ - XCTAssertEqual(mail.signedKey?.keyID, alice) - XCTAssertEqual(mail.keyID, alice) - } - if let encState = encState { - XCTAssertEqual(mail.encState, encState) - } - if let sigState = sigState { - XCTAssertEqual(mail.sigState, sigState) - } - let body = mail.body - XCTAssertEqual(body.removeNewLines(), MailTest.body.removeNewLines()) - XCTAssertTrue(MailTest.compareAdrs(adrs1: ["bob@enzevalos.de"], adrs2: mail.getReceivers())) + let incMail = IncomingMail(rawData: mailData, uID: 4, folderPath: "INBOX", flags: MCOMessageFlag.init(rawValue: 0)) + if let mail = incMail.store(keyRecord: nil) { + XCTAssertEqual(mail.isSecure, isSecure) + if mail.isSecure || mail.sigState == .ValidSignature{ + XCTAssertEqual(mail.signedKey?.keyID, alice) + XCTAssertEqual(mail.keyID, alice) + } + if let encState = encState { + XCTAssertEqual(mail.encState, encState) + } + if let sigState = sigState { + XCTAssertEqual(mail.sigState, sigState) } + let body = mail.body + XCTAssertEqual(body.removeNewLines(), MailTest.body.removeNewLines()) + XCTAssertTrue(MailTest.compareAdrs(adrs1: ["bob@enzevalos.de"], adrs2: mail.getReceivers())) } else { XCTFail() @@ -258,7 +263,8 @@ class MailTest: XCTestCase { func addAliceAndBob(addAlice: Bool) -> (alice: String, bob: String){ let aliceKeyId = importKey(file: "alicePublic", isSecretKey: false) if addAlice { - _ = datahandler.newPublicKey(keyID: aliceKeyId, cryptoType: .PGP, adr: "alice@enzevalos.de", autocrypt: true) + let b = datahandler.newPublicKey(keyID: aliceKeyId, cryptoType: .PGP, adr: "alice@enzevalos.de", autocrypt: true) + print(b.keyID) } let bobKeyId = importKey(file: "bobSecret", isSecretKey: true) _ = datahandler.newSecretKey(keyID: bobKeyId, addPk: true) @@ -327,6 +333,7 @@ class MailTest: XCTestCase { func createPGPUser(adr: String = String.random().lowercased(), name: String = String.random()) -> (MCOAddress, String) { let user = createUser(adr: adr, name: name) let id = pgp.generateKey(adr: user.mailbox) + print(id) return (user, id) }