Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Returning the original message in case of decryption failure #151

Merged
merged 4 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions PubNub.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@
3DACC7F72AB88F8E00210B14 /* Data+CommonCrypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DACC7F62AB88F8E00210B14 /* Data+CommonCrypto.swift */; };
3DBB2C212ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBB2C202ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift */; };
3DBB2C222ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DBB2C202ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift */; };
3DFB01942B0E30EE00146B57 /* subscription_encrypted_message_success.json in Resources */ = {isa = PBXBuildFile; fileRef = 3DFB01932B0E30EE00146B57 /* subscription_encrypted_message_success.json */; };
4C2A8D84BCD39B07A66FD9B4 /* Pods_PubNubContractTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1E7F3D449F2D66FC29674EF6 /* Pods_PubNubContractTests.framework */; };
79407BD2271D4CFA0032076C /* PubNubContractTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79407BBF271D4CFA0032076C /* PubNubContractTestCase.swift */; };
79407BD3271D4CFA0032076C /* PubNubContractTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79407BBF271D4CFA0032076C /* PubNubContractTestCase.swift */; };
Expand Down Expand Up @@ -924,6 +925,7 @@
3DACC7F62AB88F8E00210B14 /* Data+CommonCrypto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Data+CommonCrypto.swift"; sourceTree = "<group>"; };
3DBB2C202ABD8053008A100E /* PubNubCryptoModuleContractTestSteps.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PubNubCryptoModuleContractTestSteps.swift; sourceTree = "<group>"; };
3DE632651BA8B2E27ACFC4AD /* Pods-PubNubContractTestsBeta.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PubNubContractTestsBeta.release.xcconfig"; path = "Target Support Files/Pods-PubNubContractTestsBeta/Pods-PubNubContractTestsBeta.release.xcconfig"; sourceTree = "<group>"; };
3DFB01932B0E30EE00146B57 /* subscription_encrypted_message_success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscription_encrypted_message_success.json; sourceTree = "<group>"; };
793079152667C63700F23B72 /* CODEOWNERS */ = {isa = PBXFileReference; lastKnownFileType = text; path = CODEOWNERS; sourceTree = "<group>"; };
793079172667C63700F23B72 /* validate-yml.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = "validate-yml.js"; sourceTree = "<group>"; };
793079182667C63700F23B72 /* validate-pubnub-yml.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = "validate-pubnub-yml.yml"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1320,6 +1322,7 @@
isa = PBXGroup;
children = (
359287C423185EEE0046F7A2 /* subscription_success.json */,
3DFB01932B0E30EE00146B57 /* subscription_encrypted_message_success.json */,
35C6B6DF22F513D80054F242 /* subscription_mixed_success.json */,
359287BE23183DBB0046F7A2 /* subscription_signal_success.json */,
359287C023183E4A0046F7A2 /* subscription_presence_success.json */,
Expand Down Expand Up @@ -2637,6 +2640,7 @@
35FE941422EFB7C10051C455 /* unknownEndpointError.json in Resources */,
35FE93F122EF93A90051C455 /* serverCertificateUntrusted.json in Resources */,
35A6C79A22FBC2AD00E97CC5 /* groups_delete_success.json in Resources */,
3DFB01942B0E30EE00146B57 /* subscription_encrypted_message_success.json in Resources */,
35293A872369F0230049A71F /* addMessageAction_error_400.json in Resources */,
35FE93F622EF93A90051C455 /* cannotParseResponse.json in Resources */,
35A6C79C22FBC2D100E97CC5 /* groups_channels_add_success.json in Resources */,
Expand Down
71 changes: 58 additions & 13 deletions Sources/PubNub/Models/PubNubMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ public protocol PubNubMessage {
var metadata: JSONCodable? { get set }
/// The type of message that was received
var messageType: PubNubMessageType { get set }

/// An error (if any) occured while getting this message
var error: PubNubError? { get set }

/// Allows for transcoding between different MessageEvent types
init(from other: PubNubMessage) throws
}
Expand Down Expand Up @@ -69,16 +71,17 @@ public extension PubNubMessage {

/// The default implementation of the `PubNubMessage` protocol
public struct PubNubMessageBase: PubNubMessage, Codable, Hashable {
var concretePayload: AnyJSON
public var publisher: String?
var concreteMessageActions: [PubNubMessageActionBase]
public var channel: String
public var subscription: String?
public var published: Timetoken
var concreteMetadata: AnyJSON?

public var messageType: PubNubMessageType

public var error: PubNubError?

var concretePayload: AnyJSON
var concreteMessageActions: [PubNubMessageActionBase]
var concreteMetadata: AnyJSON?

public var payload: JSONCodable {
get { return concretePayload }
set {
Expand Down Expand Up @@ -109,7 +112,8 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable {
subscription: other.subscription,
published: other.published,
metadata: other.metadata?.codableValue,
messageType: other.messageType
messageType: other.messageType,
error: other.error
)
}

Expand All @@ -122,7 +126,8 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable {
subscription: subscribe.subscription,
published: subscribe.publishTimetoken.timetoken,
metadata: subscribe.metadata,
messageType: subscribe.messageType.asPubNubMessageType
messageType: subscribe.messageType.asPubNubMessageType,
error: subscribe.error
)
}

Expand All @@ -141,7 +146,8 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable {
subscription: nil,
published: history.timetoken,
metadata: history.meta,
messageType: history.messageType ?? .unknown
messageType: history.messageType ?? .unknown,
error: history.error
)
}

Expand All @@ -153,16 +159,55 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable {
subscription: String?,
published: Timetoken,
metadata: AnyJSON?,
messageType: PubNubMessageType = .unknown
messageType: PubNubMessageType = .unknown,
error: PubNubError? = nil
) {
concretePayload = payload
concreteMessageActions = actions
self.concretePayload = payload
self.concreteMessageActions = actions
self.publisher = publisher
self.channel = channel
self.subscription = subscription
self.published = published
concreteMetadata = metadata
self.concreteMetadata = metadata
self.messageType = messageType
self.error = error
}

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

try container.encode(self.concretePayload, forKey: .concretePayload)
try container.encodeIfPresent(self.publisher, forKey: .publisher)
try container.encode(self.concreteMessageActions, forKey: .concreteMessageActions)
try container.encode(self.channel, forKey: .channel)
try container.encodeIfPresent(self.subscription, forKey: .subscription)
try container.encode(self.published, forKey: .published)
try container.encodeIfPresent(self.concreteMetadata, forKey: .concreteMetadata)
try container.encode(self.messageType, forKey: .messageType)
}

enum CodingKeys: CodingKey {
case concretePayload
case publisher
case concreteMessageActions
case channel
case subscription
case published
case concreteMetadata
case messageType
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

self.concretePayload = try container.decode(AnyJSON.self, forKey: .concretePayload)
self.publisher = try container.decodeIfPresent(String.self, forKey: .publisher)
self.concreteMessageActions = try container.decode([PubNubMessageActionBase].self, forKey: .concreteMessageActions)
self.channel = try container.decode(String.self, forKey: .channel)
self.subscription = try container.decodeIfPresent(String.self, forKey: .subscription)
self.published = try container.decode(Timetoken.self, forKey: .published)
self.concreteMetadata = try container.decodeIfPresent(AnyJSON.self, forKey: .concreteMetadata)
self.messageType = try container.decode(PubNubMessageType.self, forKey: .messageType)
}
}

Expand Down
33 changes: 30 additions & 3 deletions Sources/PubNub/Networking/Routers/HistoryRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,36 @@ struct MessageHistoryResponseDecoder: ResponseDecoder {
timetoken: message.timetoken,
meta: message.meta,
uuid: message.uuid,
messageType: message.messageType
messageType: message.messageType,
error: nil
)
case .failure(let error):
PubNub.log.error("History message failed to decrypt due to \(error)")
messages[index] = MessageHistoryMessagePayload(
message: message.message,
timetoken: message.timetoken,
meta: message.meta,
uuid: message.uuid,
messageType: message.messageType,
error: error
)
PubNub.log.warn("History message failed to decrypt due to \(error)")
}
} else {
messages[index] = MessageHistoryMessagePayload(
message: message.message,
timetoken: message.timetoken,
meta: message.meta,
uuid: message.uuid,
messageType: message.messageType,
error: PubNubError(
.decryptionFailure,
additional: ["Cannot decrypt message due to invalid Base-64 input"]
)
)
}
}


return messages
}

Expand Down Expand Up @@ -284,21 +307,24 @@ struct MessageHistoryMessagePayload: Codable {
let uuid: String?
let messageType: PubNubMessageType?
let actions: RawMessageAction
let error: PubNubError?

init(
message: JSONCodable,
timetoken: Timetoken = 0,
meta: JSONCodable? = nil,
uuid: String?,
messageType: PubNubMessageType?,
actions: RawMessageAction = [:]
actions: RawMessageAction = [:],
error: PubNubError?
) {
self.message = message.codableValue
self.timetoken = timetoken
self.uuid = uuid
self.messageType = messageType
self.meta = meta?.codableValue
self.actions = actions
self.error = error
}

enum CodingKeys: String, CodingKey {
Expand All @@ -319,6 +345,7 @@ struct MessageHistoryMessagePayload: Codable {
messageType = try container.decodeIfPresent(PubNubMessageType.self, forKey: .messageType)
timetoken = Timetoken(try container.decode(String.self, forKey: .timetoken)) ?? 0
actions = try container.decodeIfPresent(RawMessageAction.self, forKey: .actions) ?? [:]
error = nil
}

public func encode(to encoder: Encoder) throws {
Expand Down
20 changes: 14 additions & 6 deletions Sources/PubNub/Networking/Routers/SubscribeRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,21 +120,25 @@ struct SubscribeDecoder: ResponseDecoder {
}

func decrypt(_ cryptoModule: CryptoModule, message: SubscribeMessagePayload) -> SubscribeMessagePayload {
// Convert base64 string into Data
var message = message
// Convert Base64 string into Data
if let messageData = message.payload.dataOptional {
// If a message fails we just return the original and move on
switch cryptoModule.decryptedString(from: messageData) {
case .success(let decodedString):
// Create mutable copy of payload
var message = message
message.payload = AnyJSON(reverse: decodedString)
return message
case .failure(let error):
PubNub.log.error("Subscribe message failed to decrypt due to \(error)")
PubNub.log.warn("Subscribe message failed to decrypt due to \(error)")
message.error = error
return message
}
}

message.error = PubNubError(
.decryptionFailure,
additional: ["Cannot decrypt message due to invalid Base-64 input"]
)
return message
}

Expand Down Expand Up @@ -250,6 +254,7 @@ public struct SubscribeMessagePayload: Codable, Hashable {
public let originTimetoken: SubscribeCursor?
public let publishTimetoken: SubscribeCursor
public let metadata: AnyJSON?
public var error: PubNubError?

enum CodingKeys: String, CodingKey {
case shard = "a"
Expand Down Expand Up @@ -304,7 +309,8 @@ public struct SubscribeMessagePayload: Codable, Hashable {
subscribeKey: String,
originTimetoken: SubscribeCursor?,
publishTimetoken: SubscribeCursor,
meta: AnyJSON?
meta: AnyJSON?,
error: PubNubError?
) {
self.shard = shard
self.subscription = subscription
Expand All @@ -316,7 +322,8 @@ public struct SubscribeMessagePayload: Codable, Hashable {
self.subscribeKey = subscribeKey
self.originTimetoken = originTimetoken
self.publishTimetoken = publishTimetoken
metadata = meta
self.metadata = meta
self.error = error
}

public init(from decoder: Decoder) throws {
Expand Down Expand Up @@ -349,6 +356,7 @@ public struct SubscribeMessagePayload: Codable, Hashable {
}

channel = fullChannel.trimmingPresenceChannelSuffix
error = nil
}

public func encode(to encoder: Encoder) throws {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"code": 200,
"body": {
"t": {
"t": "15614817397807903",
"r": 2
},
"m": [
{
"a": "1",
"f": 514,
"i": "db9c5e39-7c95-40f5-8d71-125765b6f561",
"p": {
"t": "15614814456537442",
"r": 2
},
"k": "demo",
"c": "TestChannel",
"d": "UE5FRAFBQ1JIEGOmGQMIMXD+91V+5hTxm7p7uEUhEEYohYLQz5fEGITC"
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ final class HistoryRouterTests: XCTestCase {
)

let testChannel = "TestChannel"

let testSingleChannel = ["TestChannel"]
let testMultiChannels = ["TestChannel", "OtherTestChannel"]
}
Expand Down Expand Up @@ -188,15 +187,14 @@ extension HistoryRouterTests {
var configWithCipher = config
configWithCipher.cryptoModule = CryptoModule.legacyCryptoModule(with: "NotTheRightKey", withRandomIV: false)

let pubnub = PubNub(configuration: config, session: sessions.session)
let pubnub = PubNub(configuration: configWithCipher, session: sessions.session)
pubnub.fetchMessageHistory(for: testMultiChannels) { result in
switch result {
case let .success((messagesByChannel, next)):
let channelMessages = messagesByChannel[self.testChannel]
XCTAssertNotNil(channelMessages)
XCTAssertEqual(
channelMessages?.first?.payload.dataOptional?.base64EncodedString(), "s3+CcEE2QZ/Lh9CaPieJnQ=="
)
XCTAssertEqual(channelMessages?.first?.payload.dataOptional?.base64EncodedString(), "s3+CcEE2QZ/Lh9CaPieJnQ==")
XCTAssertTrue((channelMessages ?? []).reduce(into: true) { $0 = $0 && $1.error?.reason == .decryptionFailure })
XCTAssertEqual(next?.start, 15_657_268_328_421_957)
case let .failure(error):
XCTFail("Fetch History request failed with error: \(error.localizedDescription)")
Expand Down
Loading