Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion FlowCrypt.xcodeproj/project.pbxproj
Original file line numberDiff line numberDiff line change
Expand Up@@ -1318,8 +1318,8 @@
isa = PBXGroup;
children = (
9F4163EC266574CB00106194 /* BackupServiceMock.swift */,
9F6F3C3B26ADFBC7005BD9C6 /* CoreComposeMessageMock.swift */,
9F6F3C6926ADFBEB005BD9C6 /* MessageGatewayMock.swift */,
9F6F3C3B26ADFBC7005BD9C6 /* CoreComposeMessageMock.swift */,
9F6F3C7526ADFC37005BD9C6 /* KeyStorageMock.swift */,
9F6F3C7C26ADFC60005BD9C6 /* ContactsServiceMock.swift */,
);
Expand Down
8 changes: 8 additions & 0 deletions FlowCrypt/Core/Core.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,10 @@ enum CoreError: Error, Equatable {
case notReady(String)
case format(String)
case keyMismatch(String)
case noMDC(String)
case badMDC(String)
case needPassphrase(String)
case wrongPassphrase(String)
// wrong value passed into a function
case value(String)

Expand All@@ -21,6 +25,10 @@ enum CoreError: Error, Equatable {
switch errorType {
case "format": self = .format(coreError.error.message)
case "key_mismatch": self = .keyMismatch(coreError.error.message)
case "no_mdc": self = .noMDC(coreError.error.message)
case "bad_mdc": self = .badMDC(coreError.error.message)
case "need_passphrase": self = .needPassphrase(coreError.error.message)
case "wrong_passphrase": self = .wrongPassphrase(coreError.error.message)
default: return nil
}
}
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -8,6 +8,7 @@

import Foundation
import Promises
import FlowCryptCommon

// MARK: - MessageAttachment
struct MessageAttachment: FileType {
Expand DownExpand Up@@ -47,6 +48,10 @@ enum MessageServiceError: Error {
}

final class MessageService {
private enum Constants {
static let encryptedAttachmentExtension = "pgp"
}

private let messageProvider: MessageProvider
private let keyService: KeyServiceType
private let passPhraseService: PassPhraseServiceType
Expand DownExpand Up@@ -90,7 +95,7 @@ final class MessageService {
reject(MessageServiceError.wrongPassPhrase(rawMimeData, passPhrase))
} else {
self.savePassPhrases(value: passPhrase, with: keys)
let processedMessage = self.processMessage(rawMimeData: rawMimeData, with: decrypted)
let processedMessage = try self.processMessage(rawMimeData: rawMimeData, with: decrypted, keys: keys)
resolve(processedMessage)
}
}
Expand DownExpand Up@@ -125,18 +130,30 @@ final class MessageService {
isEmail: true
)

let processedMessage = self.processMessage(rawMimeData: rawMimeData, with: decrypted)
let processedMessage = try self.processMessage(rawMimeData: rawMimeData, with: decrypted, keys: keys)
resolve(processedMessage)
}
}

private func processMessage(rawMimeData: Data, with decrypted: CoreRes.ParseDecryptMsg) -> ProcessedMessage {
private func processMessage(rawMimeData: Data, with decrypted: CoreRes.ParseDecryptMsg, keys: [PrvKeyInfo]) throws -> ProcessedMessage {
let decryptErrBlocks = decrypted.blocks
.filter { $0.decryptErr != nil }

let attachments = decrypted.blocks
let attachments = try decrypted.blocks
.filter(\.isAttachmentBlock)
.map(MessageAttachment.init)
.compactMap { block -> MessageAttachment? in
guard let attMeta = block.attMeta else { return nil }
var name = attMeta.name
let size = attMeta.length
var data = attMeta.data

if block.type == .encryptedAtt {
data = (try core.decryptFile(encrypted: data, keys: keys, msgPwd: nil).content)
name = name.deletingPathExtension
}

return MessageAttachment(name: name, size: size, data: data)
}

let messageType: ProcessedMessage.MessageType
let text: String
Expand Down
83 changes: 82 additions & 1 deletion FlowCryptAppTests/Core/FlowCryptCoreTests.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -128,6 +128,43 @@ class FlowCryptCoreTests: XCTestCase {
XCTAssertNotNil(mime.range(of: "Subject: \(msg.subject)")) // has mime Subject header
XCTAssertNil(mime.range(of: "In-Reply-To")) // Not a reply
}

func testComposeEmailInlineWithAttachment() throws {

let initialFileName = "data.txt"
let urlPath = URL(fileURLWithPath: Bundle(for: type(of: self))
.path(forResource: "data", ofType: "txt")!)
let fileData = try! Data(contentsOf: urlPath, options: .dataReadingMapped)

let attachment = SendableMsg.Attachment(
name: initialFileName, type: "text/plain",
base64: fileData.base64EncodedString()
)

let msg = SendableMsg(
text: "this is the message",
to: ["email@hello.com"], cc: [], bcc: [],
from: "sender@hello.com",
subject: "subj", replyToMimeMsg: nil,
atts: [attachment], pubKeys: nil
)
let expectation = XCTestExpectation()

var mime: String = ""
core.composeEmail(msg: msg, fmt: .encryptInline, pubKeys: [TestData.k0.pub, TestData.k1.pub])
.sinkFuture(
receiveValue: { composeEmailRes in
mime = String(data: composeEmailRes.mimeEncoded, encoding: .utf8)!
expectation.fulfill()
}, receiveError: {_ in }
)
.store(in: &cancellable)
wait(for: [expectation], timeout: 3)
XCTAssertNil(mime.range(of: msg.text)) // text encrypted
XCTAssertNotNil(mime.range(of: "Content-Type: application/pgp-encrypted")) // encrypted
XCTAssertNotNil(mime.range(of: "name=\(attachment.name)")) // attachment
XCTAssertNotNil(mime.range(of: "Subject: \(msg.subject)")) // has mime Subject header
}

func testEndToEnd() throws {
let passphrase = "some pass phrase test"
Expand DownExpand Up@@ -290,7 +327,51 @@ class FlowCryptCoreTests: XCTestCase {
XCTAssertNotNil(message.range(of: "Missing appropriate key"))
}
}


func testDecryptEncryptedFile() throws {
// Given
let initialFileName = "data.txt"
let urlPath = URL(fileURLWithPath: Bundle(for: type(of: self))
.path(forResource: "data", ofType: "txt")!)
let fileData = try! Data(contentsOf: urlPath, options: .dataReadingMapped)

let passphrase = "some pass phrase test"
let email = "e2e@domain.com"
let generateKeyRes = try core.generateKey(
passphrase: passphrase,
variant: KeyVariant.curve25519,
userIds: [UserId(email: email, name: "End to end")]
)
let k = generateKeyRes.key
let keys = [
PrvKeyInfo(
private: k.private!,
longid: k.ids[0].longid,
passphrase: passphrase,
fingerprints: k.fingerprints
)
]

// When
do {
let encrypted = try core.encryptFile(
pubKeys: [k.public],
fileData: fileData,
name: initialFileName
)
let decrypted = try self.core.decryptFile(
encrypted: encrypted.encryptedFile,
keys: keys,
msgPwd: nil
)
// Then
XCTAssertEqual(decrypted.name, initialFileName)
XCTAssertEqual(decrypted.content.count, fileData.count)
} catch {
XCTFail("Core file decryption should not fail")
}
}

func testException() throws {
do {
_ = try core.decryptKey(armoredPrv: "not really a key", passphrase: "whatnot")
Expand Down
4 changes: 4 additions & 0 deletions FlowCryptCommon/Extensions/StringExtensions.swift
Original file line numberDiff line numberDiff line change
Expand Up@@ -45,6 +45,10 @@ public extension String {
}
}
}

var deletingPathExtension: String {
return NSString(string: self).deletingPathExtension as String
}
}

public extension NSAttributedString {
Expand Down