diff --git a/enzevalos_iphone.xcodeproj/project.pbxproj b/enzevalos_iphone.xcodeproj/project.pbxproj index 4bbcfc0c95665a66bd66f9cc07acca454c845d83..974a3f6adaa91a6f25cb39d122469d3b6797f918 100644 --- a/enzevalos_iphone.xcodeproj/project.pbxproj +++ b/enzevalos_iphone.xcodeproj/project.pbxproj @@ -549,6 +549,7 @@ 47F867E32052B49800AA832F /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; }; 48C250BB32BF11B683003BA1 /* Pods-enzevalos_iphone-enzevalos_iphoneUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphone-enzevalos_iphoneUITests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphone-enzevalos_iphoneUITests/Pods-enzevalos_iphone-enzevalos_iphoneUITests.debug.xcconfig"; sourceTree = "<group>"; }; 66E758F271CD65AB3E5FE7A7 /* Pods-enzevalos_iphoneUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneUITests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphoneUITests/Pods-enzevalos_iphoneUITests.debug.xcconfig"; sourceTree = "<group>"; }; + 670159DF240FB4E800797FA5 /* enzevalos_iphone 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "enzevalos_iphone 9.xcdatamodel"; sourceTree = "<group>"; }; 6EBCCD02AD3B95D8317810E2 /* Pods-enzevalos_iphoneTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-enzevalos_iphoneTests.debug.xcconfig"; path = "../enzevalos_iphone_workspace/Pods/Target Support Files/Pods-enzevalos_iphoneTests/Pods-enzevalos_iphoneTests.debug.xcconfig"; sourceTree = "<group>"; }; 71DF08972421520D00162B74 /* EmailStringExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailStringExtensionTests.swift; sourceTree = "<group>"; }; 71DFE5B9240679E80042019C /* HeaderExtractionValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderExtractionValues.swift; sourceTree = "<group>"; }; @@ -2673,6 +2674,7 @@ A135267F1D955BDF00D3BFE1 /* enzevalos_iphone.xcdatamodeld */ = { isa = XCVersionGroup; children = ( + 670159DF240FB4E800797FA5 /* enzevalos_iphone 9.xcdatamodel */, 4751C6FD2344D169006B2A4D /* enzevalos_iphone 8.xcdatamodel */, 4751C6F0233CE1E4006B2A4D /* enzevalos_iphone 7.xcdatamodel */, A1909719220F887D00FA7B2A /* enzevalos_iphone 6.xcdatamodel */, @@ -2682,7 +2684,7 @@ 47B2318A1F0D458100961B28 /* enzevalos_iphone 2.xcdatamodel */, A13526801D955BDF00D3BFE1 /* enzevalos_iphone.xcdatamodel */, ); - currentVersion = 4751C6FD2344D169006B2A4D /* enzevalos_iphone 8.xcdatamodel */; + currentVersion = 670159DF240FB4E800797FA5 /* enzevalos_iphone 9.xcdatamodel */; path = enzevalos_iphone.xcdatamodeld; sourceTree = "<group>"; versionGroupType = wrapper.xcdatamodel; diff --git a/enzevalos_iphone/DataHandler.swift b/enzevalos_iphone/DataHandler.swift index c0556cbda925d11c84f388d911eca264e2f30cc7..402f99c00c383dced15b6323e628c6a4c7ec60c5 100644 --- a/enzevalos_iphone/DataHandler.swift +++ b/enzevalos_iphone/DataHandler.swift @@ -297,6 +297,20 @@ class DataHandler { } } + func deleteRelatedSecretKeys() { + let keys = findSecretKeys() + let pgp = SwiftPGP() + pgp.deleteSecretKeys() + for key in keys { + if let mails = key.relatedDecryptedMails as? Set<PersistentMail> { + for mail in mails { + mail.relatedSecrectKey = nil + } + } + delete(key: key) + } + } + func deleteMail(with uid: UInt64) { self.deleteNum("PersistentMail", type: "uid", search: uid) } @@ -866,7 +880,7 @@ class DataHandler { } // -------- End handle to, cc, from addresses -------- - func createMail(_ uid: UInt64, sender: MCOAddress?, receivers: [MCOAddress], cc: [MCOAddress], time: Date, received: Bool, subject: String, body: String?, readableAttachments: Set<TempAttachment> = Set<TempAttachment>(), flags: MCOMessageFlag, record: KeyRecord?, autocrypt: Autocrypt?, decryptedData: CryptoObject?, folderPath: String, secretKey: String?, references: [String] = [], mailagent: String? = nil, messageID: String? = nil, encryptedBody: String?, storeEncrypted: Bool = false) -> PersistentMail? { + func createMail(_ uid: UInt64, sender: MCOAddress?, receivers: [MCOAddress], cc: [MCOAddress], time: Date, received: Bool, subject: String, body: String?, readableAttachments: Set<TempAttachment> = Set<TempAttachment>(), flags: MCOMessageFlag, record: KeyRecord?, autocrypt: Autocrypt?, decryptedData: CryptoObject?, folderPath: String, secretKey: String?, references: [String] = [], mailagent: String? = nil, messageID: String? = nil, encryptedBody: String?, storeEncrypted: Bool = false, isKeyImported: Bool = true, attachedSignature: Data? = nil) -> PersistentMail? { guard let sender = sender else { return nil @@ -897,6 +911,7 @@ class DataHandler { } else { mail.body = nil } + // relatedSecretKey mail.secretKey = secretKey mail.folder = myfolder @@ -915,7 +930,7 @@ class DataHandler { mail.unableToDecrypt = false mail.storeEncrypted = storeEncrypted mail.received = received - + mail.attachedSignature = attachedSignature handleFromAddress(sender, fromMail: mail, autocrypt: autocrypt) diff --git a/enzevalos_iphone/OutgoingMail.swift b/enzevalos_iphone/OutgoingMail.swift index 797b0405079748539729f290c501a50d441b889f..52904e4394bb752a37498454792a2f42634242e9 100644 --- a/enzevalos_iphone/OutgoingMail.swift +++ b/enzevalos_iphone/OutgoingMail.swift @@ -46,6 +46,7 @@ class OutgoingMail { private let textContent: String? private var htmlContent: String? = nil private var attachments: [MCOAttachment] = [] + private var attachedSignature: Data? var pgpData: Data? { get { return createEncData() @@ -115,6 +116,13 @@ class OutgoingMail { self.sendEncryptedIfPossible = sendEncryptedIfPossible self.attachments = attachments self.orderReceivers() + if let attachment = attachments.first { + let signature = OutgoingMail.extractPGPSignature(attachment: attachment) + if let signatureData = signature.first?.data(using: .utf8) { + self.attachedSignature = signatureData + } + } + } init(mail: PersistentMail){ @@ -129,6 +137,7 @@ class OutgoingMail { self.orderReceivers() self.mail = mail self.loggingMail = mail.isCorrectlySigned //TODO FIX HERE + self.attachedSignature = mail.attachedSignature } func store() -> PersistentMail? { @@ -137,6 +146,7 @@ class OutgoingMail { mail?.decryptedBody = htmlContent mail?.isEncrypted = sendEncryptedIfPossible mail?.isCorrectlySigned = self.loggingMail + mail?.attachedSignature = self.attachedSignature self.mail = mail return mail } @@ -246,7 +256,15 @@ class OutgoingMail { return true } - + private static func extractPGPSignature(attachment: MCOAttachment) -> [String] { + if let content = attachment.decodedString() { + return PGPPart.SIGNATURE.findPGPPartInString(content: content) + } + else if let content = String(data: attachment.data, encoding: .ascii){ + return PGPPart.SIGNATURE.findPGPPartInString(content: content) + } + return [] + } private func createPlainData() -> Data { let builder = createBuilder() diff --git a/enzevalos_iphone/PersistentMail +CoreDataProperties.swift b/enzevalos_iphone/PersistentMail +CoreDataProperties.swift index 1612768097f47a5f0530f3144a7517a842d87b13..16d1b3fb2efb0788c1eabe2de582d9e0cfbd5281 100644 --- a/enzevalos_iphone/PersistentMail +CoreDataProperties.swift +++ b/enzevalos_iphone/PersistentMail +CoreDataProperties.swift @@ -79,10 +79,20 @@ extension PersistentMail { } } + /** + Default value: True (in case no key was in mail is provided) + True: Provided key in mail body is imported or discareded. + False: No actions have be done yet + */ + @NSManaged public var isKeyImported: Bool /** Please use encryption state */ @NSManaged public var isEncrypted: Bool + /** + Some Mail have the signature attached to them separately + */ + @NSManaged public var attachedSignature: Data? /** This mail is signed but check isSigned, too. We recommand to use signature state @@ -149,7 +159,10 @@ extension PersistentMail { Only for traveler scenario. */ @NSManaged public var decryptedKey: PersistentKey? - + /** + Related secrect Key for mail. + */ + @NSManaged public var relatedSecrectKey: SecretKey? /** Sender mail client. */ diff --git a/enzevalos_iphone/SecretKey+CoreDataProperties.swift b/enzevalos_iphone/SecretKey+CoreDataProperties.swift index 47d468117e3f0405a370242a77bf88c526e708ac..9c3470fad3931cbd50fc232aec613e3d2ec1e7c4 100644 --- a/enzevalos_iphone/SecretKey+CoreDataProperties.swift +++ b/enzevalos_iphone/SecretKey+CoreDataProperties.swift @@ -44,6 +44,10 @@ extension SecretKey { A Set of mails where as the key was used for decryption. */ @NSManaged public var decryptedMails: NSSet? + /** + A Set of mails where the relatedSecrectKey was used for decryption. + */ + @NSManaged public var relatedDecryptedMails: NSSet? } diff --git a/enzevalos_iphone/SwiftPGP.swift b/enzevalos_iphone/SwiftPGP.swift index e4ab8e5ce5b4a77508473163fdc658d040ba10e4..c240656c81588ab3c53557a52ff18c4c8f1e4368 100644 --- a/enzevalos_iphone/SwiftPGP.swift +++ b/enzevalos_iphone/SwiftPGP.swift @@ -667,7 +667,7 @@ class SwiftPGP: Encryption { return } // Try to verify mail signature - sigState = verifySignature(data: data, attachedSignature: nil, keys: key) + sigState = verifySignature(data: data, attachedSignature: mail.attachedSignature, keys: key) if sigState == SignatureState.ValidSignature { // Update database mail.isSigned = true diff --git a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/.xccurrentversion b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/.xccurrentversion index b24719e7c69bc07e985d15759e78b50ea73a1a2e..8723d24cbe7999b90840d81bec8c2e4809b58ee6 100644 --- a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/.xccurrentversion +++ b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/.xccurrentversion @@ -3,6 +3,6 @@ <plist version="1.0"> <dict> <key>_XCCurrentVersionName</key> - <string>enzevalos_iphone 8.xcdatamodel</string> + <string>enzevalos_iphone 9.xcdatamodel</string> </dict> </plist> diff --git a/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 9.xcdatamodel/contents b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 9.xcdatamodel/contents new file mode 100644 index 0000000000000000000000000000000000000000..a537bc2263e93c6d9eedf4ba3c0983aa767712d7 --- /dev/null +++ b/enzevalos_iphone/enzevalos_iphone.xcdatamodeld/enzevalos_iphone 9.xcdatamodel/contents @@ -0,0 +1,180 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="15702" systemVersion="19D76" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> + <entity name="Account" representedClassName="Account" syncable="YES" codeGenerationType="class"> + <attribute name="archiveFolderPath" attributeType="String"/> + <attribute name="displayName" attributeType="String"/> + <attribute name="draftFolderPath" attributeType="String"/> + <attribute name="inboxFolderPath" attributeType="String"/> + <attribute name="loginName" attributeType="String"/> + <attribute name="prefEnc" optional="YES" attributeType="String"/> + <attribute name="prefMailAdr" optional="YES" attributeType="String"/> + <attribute name="prefSecretKeyID" optional="YES" attributeType="String"/> + <attribute name="sentFolderPath" attributeType="String"/> + <attribute name="trashFolderPath" attributeType="String"/> + <relationship name="aliase" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="account" inverseEntity="Mail_Address"/> + <relationship name="imap" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Server" inverseName="imap" inverseEntity="Server"/> + <relationship name="keys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="SecretKey" inverseName="account" inverseEntity="SecretKey"/> + <relationship name="smtp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Server" inverseName="smtp" inverseEntity="Server"/> + </entity> + <entity name="Attachment" representedClassName="Attachment" syncable="YES" codeGenerationType="class"> + <attribute name="contentID" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="data" attributeType="Binary"/> + <attribute name="encryptionState" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="isExplicitAttachment" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="mcoPartType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="mimeType" attributeType="String"/> + <attribute name="name" attributeType="String"/> + <attribute name="signatureState" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <relationship name="mail" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="attachments" inverseEntity="PersistentMail"/> + <relationship name="parentOf" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Attachment" inverseName="partOf" inverseEntity="Attachment"/> + <relationship name="partOf" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Attachment" inverseName="parentOf" inverseEntity="Attachment"/> + </entity> + <entity name="EnzevalosContact" representedClassName="EnzevalosContact" syncable="YES"> + <attribute name="cnidentifier" optional="YES" attributeType="String"/> + <attribute name="color" optional="YES" attributeType="Transformable" customClassName="UIColor"/> + <attribute name="displayname" attributeType="String"/> + <relationship name="addresses" toMany="YES" deletionRule="Cascade" destinationEntity="Mail_Address" inverseName="contact" inverseEntity="Mail_Address"/> + <relationship name="keyrecords" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="KeyRecord" inverseName="contact" inverseEntity="KeyRecord"/> + </entity> + <entity name="Folder" representedClassName="Folder" syncable="YES"> + <attribute name="delimiter" optional="YES" attributeType="String"/> + <attribute name="flags" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="icon" optional="YES" attributeType="String"/> + <attribute name="lastUpdate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="maxID" optional="YES" attributeType="Decimal" defaultValueString="1"/> + <attribute name="minUID" optional="YES" attributeType="Decimal" defaultValueString="0.0"/> + <attribute name="path" attributeType="String"/> + <attribute name="pseudonym" attributeType="String"/> + <attribute name="uidvalidity" optional="YES" attributeType="Decimal" defaultValueString="0.0"/> + <relationship name="keyRecords" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="KeyRecord" inverseName="folder" inverseEntity="KeyRecord"/> + <relationship name="mails" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PersistentMail" inverseName="folder" inverseEntity="PersistentMail"/> + <relationship name="parent" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Folder" inverseName="subfolder" inverseEntity="Folder"/> + <relationship name="subfolder" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Folder" inverseName="parent" inverseEntity="Folder"/> + </entity> + <entity name="KeyRecord" representedClassName="KeyRecord" syncable="YES"> + <attribute name="newestDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="nick" optional="YES" attributeType="String"/> + <relationship name="contact" maxCount="1" deletionRule="Nullify" destinationEntity="EnzevalosContact" inverseName="keyrecords" inverseEntity="EnzevalosContact"/> + <relationship name="folder" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Folder" inverseName="keyRecords" inverseEntity="Folder"/> + <relationship name="key" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="record" inverseEntity="PersistentKey"/> + <relationship name="persistentMails" optional="YES" toMany="YES" deletionRule="Nullify" ordered="YES" destinationEntity="PersistentMail" inverseName="record" inverseEntity="PersistentMail"/> + </entity> + <entity name="Mail_Address" representedClassName="Mail_Address" syncable="YES"> + <attribute name="address" attributeType="String" defaultValueString=""""/> + <attribute name="invitations" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="primaryKeyID" optional="YES" attributeType="String"/> + <attribute name="pseudonym" attributeType="String"/> + <relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="aliase" inverseEntity="Account"/> + <relationship name="bcc" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="bcc" inverseEntity="PersistentMail"/> + <relationship name="cc" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="cc" inverseEntity="PersistentMail"/> + <relationship name="contact" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="EnzevalosContact" inverseName="addresses" inverseEntity="EnzevalosContact"/> + <relationship name="from" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="from" inverseEntity="PersistentMail"/> + <relationship name="keys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="mailaddress" inverseEntity="PersistentKey"/> + <relationship name="to" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="to" inverseEntity="PersistentMail"/> + </entity> + <entity name="PersistentKey" representedClassName="PersistentKey" syncable="YES"> + <attribute name="autocryptGossip" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="discoveryDate" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="encryptionType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="gossip_timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="gotFailedCallForUse" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="isMisstrusted" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="isRepealed" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="keyID" attributeType="String"/> + <attribute name="lastSeen" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="lastSeenAutocrypt" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="origin" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="preferEncryption" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="pseudonym" attributeType="String"/> + <attribute name="sentOwnPublicKey" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="verifiedDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <relationship name="activeKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey"/> + <relationship name="activeRepeal" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail"/> + <relationship name="childKeys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="parentKey" inverseEntity="PersistentKey"/> + <relationship name="firstMail" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="attachedKeys" inverseEntity="PersistentMail"/> + <relationship name="mailaddress" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="keys" inverseEntity="Mail_Address"/> + <relationship name="parentKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="childKeys" inverseEntity="PersistentKey"/> + <relationship name="record" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="KeyRecord" inverseName="key" inverseEntity="KeyRecord"/> + <relationship name="repealedByMails" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="repealsKey" inverseEntity="PersistentMail"/> + <relationship name="signedMails" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="signedKey" inverseEntity="PersistentMail"/> + </entity> + <entity name="PersistentMail" representedClassName="PersistentMail" syncable="YES"> + <attribute name="attachedSignature" optional="YES" attributeType="Binary"/> + <attribute name="body" optional="YES" attributeType="String"/> + <attribute name="date" attributeType="Date" defaultDateTimeInterval="-31582140" usesScalarValueType="NO"/> + <attribute name="decryptedBody" optional="YES" attributeType="String"/> + <attribute name="decrytionCode" optional="YES" attributeType="String"/> + <attribute name="deleteWhileTravel" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="encryptedBody" optional="YES" attributeType="String"/> + <attribute name="flag" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="gmailMessageID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="gmailThreadID" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="isCorrectlySigned" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="isEncrypted" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="isKeyImported" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="isSigned" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="keyID" optional="YES" attributeType="String"/> + <attribute name="messageID" optional="YES" attributeType="String"/> + <attribute name="modSeqValue" optional="YES" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="notLoadedMessageIDs" optional="YES" attributeType="String"/> + <attribute name="received" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES"/> + <attribute name="secretKey" optional="YES" attributeType="String"/> + <attribute name="storeEncrypted" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="subject" attributeType="String"/> + <attribute name="trouble" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> + <attribute name="uid" attributeType="Decimal" defaultValueString="0"/> + <attribute name="uidvalidity" optional="YES" attributeType="Decimal" defaultValueString="0.0"/> + <attribute name="unableToDecrypt" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="visibleBody" optional="YES" attributeType="String"/> + <attribute name="xMailer" optional="YES" attributeType="String"/> + <relationship name="attachedKeys" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="firstMail" inverseEntity="PersistentKey"/> + <relationship name="attachments" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Attachment" inverseName="mail" inverseEntity="Attachment"/> + <relationship name="bcc" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="bcc" inverseEntity="Mail_Address"/> + <relationship name="cc" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="cc" inverseEntity="Mail_Address"/> + <relationship name="decryptedKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SecretKey" inverseName="decryptedMails" inverseEntity="SecretKey"/> + <relationship name="folder" maxCount="1" deletionRule="Nullify" destinationEntity="Folder" inverseName="mails" inverseEntity="Folder"/> + <relationship name="from" maxCount="1" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="from" inverseEntity="Mail_Address"/> + <relationship name="record" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="KeyRecord" inverseName="persistentMails" inverseEntity="KeyRecord"/> + <relationship name="referenceMails" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="referenceMails" inverseEntity="PersistentMail"/> + <relationship name="relatedSecretKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SecretKey" inverseName="relatedDecryptedMails" inverseEntity="SecretKey"/> + <relationship name="repealsKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="repealedByMails" inverseEntity="PersistentKey"/> + <relationship name="signedKey" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentKey" inverseName="signedMails" inverseEntity="PersistentKey"/> + <relationship name="to" toMany="YES" deletionRule="Nullify" destinationEntity="Mail_Address" inverseName="to" inverseEntity="Mail_Address"/> + <fetchIndex name="byDateIndex"> + <fetchIndexElement property="date" type="Binary" order="ascending"/> + </fetchIndex> + </entity> + <entity name="SecretKey" representedClassName="SecretKey" syncable="YES"> + <attribute name="exported" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="importedDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/> + <attribute name="keyID" attributeType="String" defaultValueString="no"/> + <attribute name="obsolete" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/> + <attribute name="origin" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <relationship name="account" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="keys" inverseEntity="Account"/> + <relationship name="decryptedMails" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="decryptedKey" inverseEntity="PersistentMail"/> + <relationship name="relatedDecryptedMails" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PersistentMail" inverseName="relatedSecretKey" inverseEntity="PersistentMail"/> + </entity> + <entity name="Server" representedClassName="Server" syncable="YES" codeGenerationType="class"> + <attribute name="authType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="connectionType" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/> + <attribute name="hostname" attributeType="String"/> + <attribute name="port" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> + <relationship name="imap" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="imap" inverseEntity="Account"/> + <relationship name="smtp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Account" inverseName="smtp" inverseEntity="Account"/> + </entity> + <fetchRequest name="allKeyRecords" entity="KeyRecord" predicateString="NOT (FALSEPREDICATE)" returnDistinctResults="YES"/> + <fetchRequest name="getFolder" entity="Folder" predicateString="name == "$folder""/> + <fetchRequest name="getMailAddress" entity="Mail_Address" predicateString="address == "$adr""/> + <elements> + <element name="Account" positionX="-315" positionY="-36" width="128" height="255"/> + <element name="Attachment" positionX="-315" positionY="-36" width="128" height="210"/> + <element name="EnzevalosContact" positionX="-209" positionY="198" width="128" height="120"/> + <element name="Folder" positionX="-297" positionY="-18" width="128" height="240"/> + <element name="KeyRecord" positionX="-315" positionY="-36" width="128" height="135"/> + <element name="Mail_Address" positionX="-297" positionY="-18" width="128" height="210"/> + <element name="PersistentKey" positionX="-315" positionY="-36" width="128" height="405"/> + <element name="PersistentMail" positionX="-416" positionY="-189" width="128" height="658"/> + <element name="SecretKey" positionX="-306" positionY="-27" width="128" height="163"/> + <element name="Server" positionX="-306" positionY="-27" width="128" height="135"/> + </elements> +</model> \ No newline at end of file diff --git a/enzevalos_iphone/mail/IncomingMail.swift b/enzevalos_iphone/mail/IncomingMail.swift index 1b63dd842c058138db22097eb961c71231b7e283..77eb67af441dd2e22bafd0652ff5a7e4bdf45c1d 100644 --- a/enzevalos_iphone/mail/IncomingMail.swift +++ b/enzevalos_iphone/mail/IncomingMail.swift @@ -155,6 +155,7 @@ class IncomingMail { return keyIds } } + private var attachedSignature: Data? = nil private var subject: String = "" private var date: Date = Date() private var autocrypt: Autocrypt? = nil @@ -181,6 +182,7 @@ class IncomingMail { private var body: String = "" private var encryptedBody: String? private var secretKeys: [String] = [] + private var relaedSecrectKey: SecretKey? = nil private var newPublicKeys: [String] = [] private var newAutocrypPublicKeys: [String] = [] private var newSecretKeyIDs: [String]? = [] @@ -264,7 +266,7 @@ class IncomingMail { func store(keyRecord: KeyRecord?) -> PersistentMail? { let sk = secretKeys.first //TODO FIX: may import more secret keys? - let mail = DataHandler.handler.createMail(uID, sender: from, receivers: rec, cc: cc, time: date, received: true, subject: subject, body: body, readableAttachments: readableAttachments, flags: flags, record: keyRecord, autocrypt: autocrypt, decryptedData: cryptoObj, folderPath: folderPath, secretKey: sk, references: references, mailagent: userAgent, messageID: msgID, encryptedBody: encryptedBody, storeEncrypted: storeEncrypted) + let mail = DataHandler.handler.createMail(uID, sender: from, receivers: rec, cc: cc, time: date, received: true, subject: subject, body: body, readableAttachments: readableAttachments, flags: flags, record: keyRecord, autocrypt: autocrypt, decryptedData: cryptoObj, folderPath: folderPath, secretKey: sk, references: references, mailagent: userAgent, messageID: msgID, encryptedBody: encryptedBody, storeEncrypted: storeEncrypted, attachedSignature: attachedSignature) if let m = mail { let pgp = SwiftPGP() if let autoc = autocrypt, let adr = from?.mailbox { @@ -375,6 +377,7 @@ class IncomingMail { signedStrings = IncomingMail.extractSignedMessage(text: body) inlineSigned = true } + var text: String? var signedObject: CryptoObject? if signedStrings.count == 1 { @@ -383,7 +386,11 @@ class IncomingMail { else if signedStrings.count > 1 { text = signedStrings.joined(separator: "\r\n") } - if let signedData = text?.data(using: .utf8){ // No Signed Data! + if let signedData = text?.data(using: .utf8) { // No Signed Data! + // First Signature as main attachedSignature for mail -- not optimal + if let sig = signaturesRaw.first { + self.attachedSignature = try? Armor.readArmored(sig) + } for sig in signaturesRaw { if let signature = try? Armor.readArmored(sig), let adr = from?.mailbox { for id in fromKeyIds { diff --git a/enzevalos_iphoneTests/CryptoTests.swift b/enzevalos_iphoneTests/CryptoTests.swift index a3695a8eeb2efb5542ee570b0384ff8854959d89..06cbf1c028d152034c6a43823869733a5ea7082e 100644 --- a/enzevalos_iphoneTests/CryptoTests.swift +++ b/enzevalos_iphoneTests/CryptoTests.swift @@ -591,6 +591,76 @@ class CryptoTests: XCTestCase { return "" } + func testMail(from: MCOAddress, to: [MCOAddress], cc: [MCOAddress], bcc: [MCOAddress], flags: MCOMessageFlag = MCOMessageFlag.init(rawValue: 0), folder: String = "INBOX", date: Date = Date(timeIntervalSince1970: TimeInterval(arc4random())), cryptoObject: CryptoObject? = nil, body: String = String.random(length: 20)) -> PersistentMail? { + + let subject = String.random(length: 20) + let id = UInt64(arc4random()) + var body = body + + if let decryptedBody = cryptoObject?.decryptedText { + body = decryptedBody + } + var mail: PersistentMail? + mail = datahandler.createMail(id, sender: from, receivers: to, cc: cc, time: date, received: true, subject: subject, body: body, flags: flags, record: nil, autocrypt: nil, decryptedData: cryptoObject, folderPath: folder, secretKey: nil, encryptedBody: cryptoObject?.chiperString) + XCTAssertNotNil(mail) + XCTAssertEqual(mail?.body, body) + XCTAssertEqual(mail?.subject, subject) + XCTAssertEqual(mail?.folder.name.lowercased(), folder.lowercased()) + + return mail + } + + // Not finished yet + func testfindNotSignedMailForPublicKey() { + // E-Mail generieren + guard let from = MCOAddress(mailbox: "alice@example.com") else { + return + } + // Create test Mail + guard let mail = testMail(from: from, to: [user], cc: [], bcc: []) else { + XCTFail("No test mail") + return + } + // import keys and data for signature test + guard let keys = try? pgp.importKeys(key: keyForSignedMessage, pw: nil, isSecretKey: true, autocrypt: false), keys.count > 0 else { + XCTFail("Can not import key") + return + } + guard let signedData = try? Armor.readArmored(signedMessage) else { + XCTFail("No signed data") + return + } + guard let publickey = pgp.loadKey(id: mail.firstKey!.keyID)?.publicKey, keys.count > 0 else { + XCTFail("Can not load key") + return + } + + let text = "only a signed mail!" + + // Change Mail + mail.secretKey = keys.first + mail.isSigned = true + mail.isCorrectlySigned = false + datahandler.save(during: "") + XCTAssertTrue(mail.isSigned) + XCTAssertFalse(mail.isCorrectlySigned) + + // "un-comment" when signature is generated correctly for test email. + // pgp.findNotSignedMailForPublicKey(keyID: publickey.keyID.longIdentifier) + XCTAssertTrue(mail.isSigned) + XCTAssertTrue(mail.isCorrectlySigned) + + // try to decrypt data + if let data = text.data(using: .utf8) { + let cryptoObject = pgp.decrypt(data: data, attachedSignature: signedData, decKeyIDs: [userKeyID], signatureIDs: keys, fromAddr: "alice@enzevalos.de") + XCTAssert(cryptoObject.encryptionState == .NoEncryption && cryptoObject.signatureState == .ValidSignature, "EncState: \(cryptoObject.encryptionState) SigState: \(cryptoObject.signatureState)") + XCTAssert(cryptoObject.chiperString == text) + } + else { + XCTFail("Can not make String to data.") + } + } + func testImportMultiIDs(){ let key = importKey(file: "AliceMultiIDs (439EE43C) – Public") do {