Skip to content

Commit

Permalink
Fix/msg obj (#24)
Browse files Browse the repository at this point in the history
* feat:message content added

* reaction and reply added

* Fixed message sending and parsing (#20)

* Throw error if group DTO fetching failed with 400 error.

* Encrypt messageObj when sending the message

* Return decrypted message content if msjObj decryption failed

* Break Conversation decryption functions into simpler functions.
Added option to pass secret key for optimisation.

* Added support of media embed messages (#21)

* Throw error if group DTO fetching failed with 400 error.

* Added MediaEmbed to the enum of MessageType.
Handle MediaEmbed type when sending the message.

* chore: remove print

---------

Co-authored-by: teach.gamil.com <teach.gamil.com>
Co-authored-by: Oleg <[email protected]>
Co-authored-by: Gbogboade Ayomide <[email protected]>
  • Loading branch information
3 people committed Mar 11, 2024
1 parent 8ec7401 commit 4511399
Show file tree
Hide file tree
Showing 12 changed files with 384 additions and 151 deletions.
142 changes: 100 additions & 42 deletions Sources/Chat/Conversation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ extension PushChat {

if toDecrypt {
for i in 0..<messages.count {
let decryptedMsg = try await decryptMessage(
let (decryptedMsg, decryptedObj) = try await decryptMessage(
message: messages[i], privateKeyArmored: pgpPrivateKey, env: env)

if decryptedObj != nil {
messages[i].messageObj = decryptedObj
}
messages[i].messageContent = decryptedMsg
}
}
Expand Down Expand Up @@ -80,72 +84,126 @@ extension PushChat {
message: Message,
privateKeyArmored: String,
env: ENV = ENV.STAGING
) async throws -> String {
) async throws -> (String, String?) {
do {

if message.encType == "pgpv1:group" {
return try await decryptPrivateGroupMessage(
message, privateKeyArmored: privateKeyArmored, env: env)
}
if message.encType != "pgp" {
return message.messageContent
return (message.messageContent, nil)
}
return try decryptMessage(
let decrypytedMessage = try decryptMessage(
message.messageContent, encryptedSecret: message.encryptedSecret!,
privateKeyArmored: privateKeyArmored)

return (decrypytedMessage, nil)
} catch {
if isGroupChatId(message.toCAIP10) {
return "message encrypted before you join"
return ("message encrypted before you join", nil)
}
return "Unable to decrypt message"
return ("Unable to decrypt message", nil)
}
}

public static func decryptPrivateGroupMessage(
_ message: Message,
privateKeyArmored: String,
groupSecretKey: String? = nil,
env: ENV
) async throws -> String {
) async throws -> (String, String?) {
do {

let encryptedSecret = try await PushChat.getGroupSessionKey(
sessionKey: message.sessionKey!, env: env)

print("got enc sec \(encryptedSecret)")

return try decryptMessage(
message.messageContent,
encryptedSecret: encryptedSecret,
privateKeyArmored: privateKeyArmored
)
let secretKey: String
if let groupSecretKey {
secretKey = groupSecretKey
} else {
secretKey = try await getPrivateGroupPGPSecretKey(
sessionKey: message.sessionKey!,
privateKeyArmored: privateKeyArmored,
env: env)
}

return try decryptPrivateGroupMessage(message, using: secretKey, privateKeyArmored: privateKeyArmored, env: env)
} catch {
throw PushChat.ChatError.dectyptionFalied
}
}

public static func decryptMessage(
_ message: String,
encryptedSecret: String,
privateKeyArmored: String
) throws -> String {
do {

let secretKey = try Pgp.pgpDecrypt(
cipherText: encryptedSecret, toPrivateKeyArmored: privateKeyArmored)

guard let userMsg = try AESCBCHelper.decrypt(cipherText: message, secretKey: secretKey) else {
throw PushChat.ChatError.dectyptionFalied
}

let userMsgStr = String(data: userMsg, encoding: .utf8)

if userMsgStr == nil {
throw PushChat.ChatError.dectyptionFalied
}

return userMsgStr!
} catch {
throw PushChat.ChatError.dectyptionFalied
public static func getPrivateGroupPGPSecretKey(
sessionKey: String,
privateKeyArmored: String,
env: ENV
) async throws -> String {
let encryptedSecret = try await PushChat.getGroupSessionKey(
sessionKey: sessionKey, env: env)
return try decryptPGPSecretKey(
encryptedSecret: encryptedSecret, toPrivateKeyArmored: privateKeyArmored)
}
}

public static func decryptPrivateGroupMessage(
_ message: Message,
using secretKey: String,
privateKeyArmored: String,
env: ENV
) throws -> (String, String?) {
do {
var messageObj: String? = nil
if let msgObj = message.messageObj {
messageObj = try? decryptMessage(
msgObj, secretKey: secretKey)
}

let decMsg = try decryptMessage(
message.messageContent,
secretKey: secretKey)

return (decMsg, messageObj)
} catch {
throw PushChat.ChatError.dectyptionFalied
}
}

public static func decryptMessage(
_ message: String,
encryptedSecret: String,
privateKeyArmored: String
) throws -> String {
do {

let secretKey = try decryptPGPSecretKey(
encryptedSecret: encryptedSecret, toPrivateKeyArmored: privateKeyArmored)

return try decryptMessage(message, secretKey: secretKey)
} catch {
throw PushChat.ChatError.dectyptionFalied
}
}

public static func decryptPGPSecretKey(
encryptedSecret: String,
toPrivateKeyArmored privateKeyArmored: String
) throws -> String {
try Pgp.pgpDecrypt(
cipherText: encryptedSecret, toPrivateKeyArmored: privateKeyArmored)
}

public static func decryptMessage(
_ message: String,
secretKey: String
) throws -> String {
do {
guard let userMsg = try AESCBCHelper.decrypt(cipherText: message, secretKey: secretKey) else {
throw PushChat.ChatError.dectyptionFalied
}

if let userMsgStr = String(data: userMsg, encoding: .utf8) {
return userMsgStr
}
throw PushChat.ChatError.dectyptionFalied
} catch {
throw PushChat.ChatError.dectyptionFalied
}
}

}
4 changes: 2 additions & 2 deletions Sources/Chat/Group/GetGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ extension PushChat {
}

public static func getGroupInfoDTO(chatId: String, env: ENV) async throws -> PushChat
.PushGroupInfoDTO?
.PushGroupInfoDTO
{
let url = try PushEndpoint.getGroup(chatId: chatId, apiVersion: "v2", env: env).url
var request = URLRequest(url: url)
Expand All @@ -38,7 +38,7 @@ extension PushChat {
}

if httpResponse.statusCode == 400 {
return nil
throw URLError(.badServerResponse)
}

guard (200...299).contains(httpResponse.statusCode) else {
Expand Down
73 changes: 58 additions & 15 deletions Sources/Chat/Send.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,27 +47,68 @@ extension PushChat {
}
}

public enum MessageType: String {
case Text = "Text"
case Image = "Image"
case Reaction = "Reaction"
case Reply = "Reply"
case MediaEmbed = "MediaEmbed"
}

public struct SendOptions {
public var messageContent = ""
public var messageType = "Text"
public var messageType: MessageType
public var receiverAddress: String
public var account: String
public var pgpPrivateKey: String
public var senderPgpPubicKey: String?
public var receiverPgpPubicKey: String?
public var processMessage: String?
public var reference: String?
public var env: ENV = .STAGING

public enum Reactions: String {
case THUMBSUP = "\u{1F44D}"
case THUMBSDOWN = "\u{1F44E}"
case HEART = "\u{2764}\u{FE0F}"
case CLAP = "\u{1F44F}"
case LAUGH = "\u{1F602}"
case SAD = "\u{1F622}"
case ANGRY = "\u{1F621}"
case SURPRISE = "\u{1F632}"
case FIRE = "\u{1F525}"
}

public init(
messageContent: String, messageType: String, receiverAddress: String, account: String,
pgpPrivateKey: String, env: ENV = .STAGING
pgpPrivateKey: String, refrence: String? = nil, env: ENV = .STAGING
) {
self.messageContent = messageContent
self.messageType = messageType
self.messageType = MessageType(rawValue: messageType)!
self.receiverAddress = walletToPCAIP10(account: receiverAddress)
self.account = walletToPCAIP10(account: account)
self.pgpPrivateKey = pgpPrivateKey
self.reference = refrence
self.env = env
}

public func getMessageObjJSON() throws -> String {
switch messageType {
case .Text, .Image, .MediaEmbed:
return try getJsonStringFromKV([
("content", self.messageContent)
])
case .Reaction:
return try getJsonStringFromKV([
("content", self.messageContent),
("refrence", self.reference!),
])
case .Reply:
return """
{"content":{"messageType":"Text","messageObj":{"content":"\(self.messageContent)"}},"reference":"\(self.reference!)"}
""".trimmingCharacters(in: .whitespaces)
}
}
}

static func sendIntentService(payload: SendMessagePayload, env: ENV) async throws -> Message {
Expand Down Expand Up @@ -154,19 +195,21 @@ extension PushChat {
{

var encType = "PlainText"
var (signature, messageConent) = ("", options.messageContent)
var (dep_signature, messageConent) = ("", options.messageContent)
var messageObj = try options.getMessageObjJSON()

let secretKey = try Pgp.pgpDecrypt(
cipherText: groupInfo.encryptedSecret!, toPrivateKeyArmored: options.pgpPrivateKey)

if groupInfo.encryptedSecret != nil {
// Enc message
encType = "pgpv1:group"

messageConent = try AESCBCHelper.encrypt(messageText: messageConent, secretKey: secretKey)
signature = try Pgp.sign(message: messageConent, privateKey: options.pgpPrivateKey)
dep_signature = try Pgp.sign(message: messageConent, privateKey: options.pgpPrivateKey)
messageObj = try AESCBCHelper.encrypt(messageText: messageObj, secretKey: secretKey)

} else {
signature = try signMessage(
dep_signature = try signMessage(
messageContent: messageConent, senderPgpPrivateKey: options.pgpPrivateKey)
}

Expand All @@ -175,8 +218,8 @@ extension PushChat {
("toDID", options.account),
("fromCAIP10", options.account),
("toCAIP10", options.receiverAddress),
("messageObj", messageConent),
("messageType", "Text"),
("messageObj", messageObj),
("messageType", options.messageType.rawValue),
("encType", encType),
("sessionKey", groupInfo.sessionKey!),
("encryptedSecret", "null"),
Expand All @@ -189,9 +232,9 @@ extension PushChat {
fromDID: options.account, toDID: options.receiverAddress,
fromCAIP10: options.account, toCAIP10: options.receiverAddress,
messageContent: messageConent,
messageObj: messageConent,
messageType: options.messageType,
signature: signature, encType: encType, encryptedSecret: nil, sigType: "pgpv3",
messageObj: messageObj,
messageType: options.messageType.rawValue,
signature: dep_signature, encType: encType, encryptedSecret: nil, sigType: "pgpv3",
verificationProof: "pgpv3:\(verificationProof)",
sessionKey: groupInfo.sessionKey)

Expand Down Expand Up @@ -224,7 +267,7 @@ extension PushChat {
return SendMessagePayload(
fromDID: options.account, toDID: options.receiverAddress,
fromCAIP10: options.account, toCAIP10: options.receiverAddress,
messageContent: messageConent, messageType: options.messageType,
messageContent: messageConent, messageType: options.messageType.rawValue,
signature: signature, encType: encType, encryptedSecret: encryptedSecret, sigType: "pgp")
}

Expand Down Expand Up @@ -305,7 +348,7 @@ extension PushChat {
if shouldEncrypt {
if isGroupChatId(receiverAddress) {
let groupInfo = try await PushChat.getGroupInfoDTO(
chatId: receiverAddress, env: sendOptions.env)!
chatId: receiverAddress, env: sendOptions.env)
if groupInfo.isPublic {
publicKeys = try await getGroupChatPublicKeys(sendOptions)
} else {
Expand Down Expand Up @@ -353,7 +396,7 @@ extension PushChat {
if approveOptions.isGroupChat {
// TODO: remove unwrap
let groupInfo = try await PushChat.getGroupInfoDTO(
chatId: approveOptions.toDID, env: approveOptions.env)!
chatId: approveOptions.toDID, env: approveOptions.env)
if !groupInfo.isPublic {
return try await getApprovePayloadPrivateGroup(approveOptions, groupInfo)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/Helpers/Chat/GetInboxList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ private func decryptFeeds(
throw PushChat.ChatError.decryptedPrivateKeyNecessary
}

let decryptedMsg = try await PushChat.decryptMessage(
let (decryptedMsg, _) = try await PushChat.decryptMessage(
message: currentFeed.msg!, privateKeyArmored: pgpPrivateKey!, env: env)
currentFeed.msg?.messageContent = decryptedMsg
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/Helpers/Ipfs/Cid.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public struct Message: Codable {
public var toDID: String
public var messageType: String
public var messageContent: String
public var messageObj: String?
public var signature: String
public var sigType: String
public var timestamp: Int?
Expand All @@ -19,6 +20,7 @@ public struct Message: Codable {

public func getCID(env: ENV, cid: String) async throws -> Message {
let url: URL = PushEndpoint.getCID(env: env, cid: cid).url

let (data, res) = try await URLSession.shared.data(from: url)

guard let httpResponse = res as? HTTPURLResponse else {
Expand Down
2 changes: 1 addition & 1 deletion Tests/Chat/Group/SendGroupMsgTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class GroupChatSendMsgTests: XCTestCase {
pgpPrivateKey: UserPrivateKey
))

let decrytedMessage = try await PushChat.decryptMessage(
let (decrytedMessage, _) = try await PushChat.decryptMessage(
message: msgRes, privateKeyArmored: UserPrivateKey)

XCTAssertEqual(msgRes.encType, "pgp")
Expand Down
3 changes: 2 additions & 1 deletion Tests/Chat/P2P/SendTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,8 @@ class SendChatsTests: XCTestCase {
pgpPrivateKey: UserPrivateKey
))

let res = try await PushChat.decryptMessage(message: msg, privateKeyArmored: UserPrivateKey)
let (res, _) = try await PushChat.decryptMessage(
message: msg, privateKeyArmored: UserPrivateKey)

XCTAssertEqual(res, messageToSen2)
}
Expand Down
Loading

0 comments on commit 4511399

Please sign in to comment.