Skip to content

Commit c58d58e

Browse files
authored
Fixes for PubNub client (#160)
fix(subscribe): preventing disconnection when subscribing via PubNub to a channel that was already subscribed to fix(crypto): fixes for computing a hash value in CryptoModule
1 parent 8ec1c16 commit c58d58e

File tree

16 files changed

+318
-161
lines changed

16 files changed

+318
-161
lines changed

.pubnub.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
---
22
name: swift
33
scm: github.com/pubnub/swift
4-
version: "7.0.0"
4+
version: "7.1.0"
55
schema: 1
66
changelog:
7+
- date: 2024-03-18
8+
version: 7.1.0
9+
changes:
10+
- type: bug
11+
text: "Preventing disconnection when subscribing via `PubNub` to a channel that was already subscribed to."
12+
- type: bug
13+
text: "Fixes for computing a hash value in `CryptoModule`."
714
- date: 2024-02-21
815
version: 7.0.0
916
changes:
@@ -524,7 +531,7 @@ sdks:
524531
- distribution-type: source
525532
distribution-repository: GitHub release
526533
package-name: PubNub
527-
location: https://github.com/pubnub/swift/archive/refs/tags/7.0.0.zip
534+
location: https://github.com/pubnub/swift/archive/refs/tags/7.1.0.zip
528535
supported-platforms:
529536
supported-operating-systems:
530537
macOS:

PubNub.xcodeproj/project.pbxproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3843,7 +3843,7 @@
38433843
"@loader_path/Frameworks",
38443844
);
38453845
MACOSX_DEPLOYMENT_TARGET = 10.13;
3846-
MARKETING_VERSION = 7.0.0;
3846+
MARKETING_VERSION = 7.1.0;
38473847
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
38483848
MTL_FAST_MATH = YES;
38493849
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubUser;
@@ -3892,7 +3892,7 @@
38923892
"@loader_path/Frameworks",
38933893
);
38943894
MACOSX_DEPLOYMENT_TARGET = 10.13;
3895-
MARKETING_VERSION = 7.0.0;
3895+
MARKETING_VERSION = 7.1.0;
38963896
MTL_ENABLE_DEBUG_INFO = NO;
38973897
MTL_FAST_MATH = YES;
38983898
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubUser;
@@ -3998,7 +3998,7 @@
39983998
"@loader_path/Frameworks",
39993999
);
40004000
MACOSX_DEPLOYMENT_TARGET = 10.13;
4001-
MARKETING_VERSION = 7.0.0;
4001+
MARKETING_VERSION = 7.1.0;
40024002
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
40034003
MTL_FAST_MATH = YES;
40044004
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubSpace;
@@ -4049,7 +4049,7 @@
40494049
"@loader_path/Frameworks",
40504050
);
40514051
MACOSX_DEPLOYMENT_TARGET = 10.13;
4052-
MARKETING_VERSION = 7.0.0;
4052+
MARKETING_VERSION = 7.1.0;
40534053
MTL_ENABLE_DEBUG_INFO = NO;
40544054
MTL_FAST_MATH = YES;
40554055
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubSpace;
@@ -4168,7 +4168,7 @@
41684168
"@loader_path/Frameworks",
41694169
);
41704170
MACOSX_DEPLOYMENT_TARGET = 10.13;
4171-
MARKETING_VERSION = 7.0.0;
4171+
MARKETING_VERSION = 7.1.0;
41724172
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
41734173
MTL_FAST_MATH = YES;
41744174
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubMembership;
@@ -4218,7 +4218,7 @@
42184218
"@loader_path/Frameworks",
42194219
);
42204220
MACOSX_DEPLOYMENT_TARGET = 10.13;
4221-
MARKETING_VERSION = 7.0.0;
4221+
MARKETING_VERSION = 7.1.0;
42224222
MTL_ENABLE_DEBUG_INFO = NO;
42234223
MTL_FAST_MATH = YES;
42244224
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubMembership;
@@ -4696,7 +4696,7 @@
46964696
"$(TOOLCHAIN_DIR)/usr/lib/swift/macosx",
46974697
);
46984698
MACOSX_DEPLOYMENT_TARGET = 10.13;
4699-
MARKETING_VERSION = 7.0.0;
4699+
MARKETING_VERSION = 7.1.0;
47004700
OTHER_CFLAGS = "$(inherited)";
47014701
OTHER_LDFLAGS = "$(inherited)";
47024702
OTHER_SWIFT_FLAGS = "$(inherited)";
@@ -4737,7 +4737,7 @@
47374737
"$(TOOLCHAIN_DIR)/usr/lib/swift/macosx",
47384738
);
47394739
MACOSX_DEPLOYMENT_TARGET = 10.13;
4740-
MARKETING_VERSION = 7.0.0;
4740+
MARKETING_VERSION = 7.1.0;
47414741
OTHER_CFLAGS = "$(inherited)";
47424742
OTHER_LDFLAGS = "$(inherited)";
47434743
OTHER_SWIFT_FLAGS = "$(inherited)";

PubNubSwift.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'PubNubSwift'
3-
s.version = '7.0.0'
3+
s.version = '7.1.0'
44
s.homepage = 'https://github.com/pubnub/swift'
55
s.documentation_url = 'https://www.pubnub.com/docs/swift-native/pubnub-swift-sdk'
66
s.authors = { 'PubNub, Inc.' => '[email protected]' }

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)
66
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
77
[![Build Status](https://travis-ci.org/pubnub/swift.svg?branch=master)](https://travis-ci.org/pubnub/swift)
8-
[![Codacy Coverage Grade Badge](https://api.codacy.com/project/badge/Grade/d6dbd8cad97d42bbb72c47137e94d6f5)](https://www.codacy.com?utm_source=github.com&utm_medium=referral&utm_content=pubnub/swift&utm_campaign=Badge_Grade)
8+
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/ea96a32a311944eaa09b4c452db4d397)](https://app.codacy.com?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
99

1010
This is the official PubNub Swift SDK repository.
1111

@@ -59,7 +59,7 @@ end
5959

6060
> Note: Replace `YOUR_TARGET_NAME` with your target's name.
6161
62-
In the directory containing your `Podfile`. execute the following:
62+
In the directory containing your `Podfile` execute the following:
6363

6464
```bash
6565
pod install

Sources/PubNub/Helpers/Constants.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public enum Constant {
5757

5858
static let pubnubSwiftSDKName: String = "PubNubSwift"
5959

60-
static let pubnubSwiftSDKVersion: String = "7.0.0"
60+
static let pubnubSwiftSDKVersion: String = "7.1.0"
6161

6262
static let appBundleId: String = {
6363
if let info = Bundle.main.infoDictionary,

Sources/PubNub/Helpers/Crypto/CryptoModule.swift

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,31 @@ public class CryptorModule {
1515
public static func aesCbcCryptoModule(with key: String, withRandomIV: Bool = true) -> CryptoModule {
1616
preconditionFailure("This method is no longer available")
1717
}
18+
1819
public static func legacyCryptoModule(with key: String, withRandomIV: Bool = true) -> CryptoModule {
1920
preconditionFailure("This method is no longer available")
2021
}
2122
}
2223

2324
/// Object capable of encryption/decryption
2425
public struct CryptoModule {
25-
private let defaultCryptor: Cryptor
26-
private let cryptors: [Cryptor]
26+
private let defaultCryptor: any Cryptor
27+
private let cryptors: [any Cryptor]
2728
private let legacyCryptorId: CryptorId = []
2829

2930
typealias Base64EncodedString = String
3031

3132
/// Initializes `CryptoModule` with custom ``Cryptor`` objects capable of encryption and decryption
3233
///
33-
/// Use this constructor if you would like to provide **custom** objects for decryption and encryption and don't want to use PubNub's built-in `Cryptors`.
34-
/// Otherwise, refer to convenience static factory methods such as ``aesCbcCryptoModule(with:withRandomIV:)``
35-
/// and ``legacyCryptoModule(with:withRandomIV:)`` that return `CryptoModule` configured for you.
34+
/// Use this constructor if you would like to provide **custom** objects for decryption and encryption
35+
/// and don't want to use PubNub's built-in `Cryptors`. Otherwise, refer to convenience static factory methods
36+
/// such as ``aesCbcCryptoModule(with:withRandomIV:)``and ``legacyCryptoModule(with:withRandomIV:)``
37+
/// that return `CryptoModule` configured for you.
3638
///
3739
/// - Parameters:
3840
/// - default: Primary ``Cryptor`` instance used for encryption and decryption
3941
/// - cryptors: An optional list of ``Cryptor`` instances which older messages/files were encoded
40-
public init(default cryptor: Cryptor, cryptors: [Cryptor] = []) {
42+
public init(default cryptor: any Cryptor, cryptors: [any Cryptor] = []) {
4143
self.defaultCryptor = cryptor
4244
self.cryptors = cryptors
4345
}
@@ -46,13 +48,15 @@ public struct CryptoModule {
4648
///
4749
/// - Parameters:
4850
/// - data: Data to encrypt
49-
/// - Returns: A success, storing encrypted `Data` if operation succeeds. Otherwise, a failure storing `PubNubError` is returned
51+
/// - Returns:
52+
/// - **Success**: An encrypted `Data` object
53+
/// - **Failure**: `PubNubError` describing the reason of failure
5054
public func encrypt(data: Data) -> Result<Data, PubNubError> {
5155
guard !data.isEmpty else {
5256
return .failure(PubNubError(
5357
.encryptionFailure,
54-
additional: ["Cannot encrypt empty Data"])
55-
)
58+
additional: ["Cannot encrypt empty Data"]
59+
))
5660
}
5761
return defaultCryptor.encrypt(data: data).map {
5862
if defaultCryptor.id == LegacyCryptor.ID {
@@ -71,13 +75,15 @@ public struct CryptoModule {
7175
///
7276
/// - Parameters:
7377
/// - data: Data to decrypt
74-
/// - Returns: A success, storing decrypted `Data` if operation succeeds. Otherwise, a failure storing `PubNubError` is returned
78+
/// - Returns:
79+
/// - **Success**: A decrypted `Data` object
80+
/// - **Failure**: `PubNubError` describing the reason of failure
7581
public func decrypt(data: Data) -> Result<Data, PubNubError> {
7682
guard !data.isEmpty else {
7783
return .failure(PubNubError(
7884
.decryptionFailure,
79-
additional: ["Cannot decrypt empty Data in \(String(describing: self))"])
80-
)
85+
additional: ["Cannot decrypt empty Data in \(String(describing: self))"]
86+
))
8187
}
8288
do {
8389
let header = try CryptorHeader.from(data: data)
@@ -87,7 +93,7 @@ public struct CryptoModule {
8793
.unknownCryptorFailure,
8894
additional: [
8995
"Could not find matching Cryptor for \(header.cryptorId()) while decrypting Data. " +
90-
"Ensure the corresponding instance is registered in \(String(describing: Self.self))"
96+
"Ensure the corresponding instance is registered in \(String(describing: Self.self))"
9197
]
9298
))
9399
}
@@ -115,8 +121,8 @@ public struct CryptoModule {
115121
if $0.isEmpty {
116122
return .failure(PubNubError(
117123
.decryptionFailure,
118-
additional: ["Decrypting resulted with empty Data"])
119-
)
124+
additional: ["Decrypting resulted with empty Data"]
125+
))
120126
}
121127
return .success($0)
122128
}
@@ -129,8 +135,8 @@ public struct CryptoModule {
129135
return .failure(PubNubError(
130136
.decryptionFailure,
131137
underlying: error,
132-
additional: ["Cannot decrypt InputStream"])
133-
)
138+
additional: ["Cannot decrypt InputStream"]
139+
))
134140
}
135141
}
136142

@@ -139,7 +145,9 @@ public struct CryptoModule {
139145
/// - Parameters:
140146
/// - stream: Stream to encrypt
141147
/// - contentLength: Content length of encoded stream
142-
/// - Returns: A success, storing an `InputStream` value if operation succeeds. Otherwise, a failure storing `PubNubError` is returned
148+
/// - Returns:
149+
/// - **Success**: An `InputStream` value
150+
/// - **Failure**: `PubNubError` describing the reason of failure
143151
public func encrypt(stream: InputStream, contentLength: Int) -> Result<InputStream, PubNubError> {
144152
guard contentLength > 0 else {
145153
return .failure(PubNubError(
@@ -179,7 +187,9 @@ public struct CryptoModule {
179187
/// - stream: Stream to decrypt
180188
/// - contentLength: Content length of encrypted stream
181189
/// - to: URL where the stream should be decrypted to
182-
/// - Returns: A success, storing a decrypted `InputStream` value if operation succeeds. Otherwise, a failure storing `PubNubError` is returned
190+
/// - Returns:
191+
/// - **Success**: A decrypted `InputStream` object
192+
/// - **Failure**: `PubNubError` describing the reason of failure
183193
@discardableResult
184194
public func decrypt(
185195
stream: InputStream,
@@ -203,7 +213,7 @@ public struct CryptoModule {
203213
.unknownCryptorFailure,
204214
additional: [
205215
"Could not find matching Cryptor for \(readHeaderResp.header.cryptorId()) while decrypting InputStream. " +
206-
"Ensure the corresponding instance is registered in \(String(describing: Self.self))"
216+
"Ensure the corresponding instance is registered in \(String(describing: Self.self))"
207217
]
208218
))
209219
}
@@ -218,8 +228,8 @@ public struct CryptoModule {
218228
if outputPath.sizeOf == 0 {
219229
return .failure(PubNubError(
220230
.decryptionFailure,
221-
additional: ["Decrypting resulted with an empty File"])
222-
)
231+
additional: ["Decrypting resulted with an empty File"]
232+
))
223233
}
224234
return .success($0)
225235
}
@@ -237,7 +247,7 @@ public struct CryptoModule {
237247
}
238248
}
239249

240-
private func cryptor(matching header: CryptorHeader) -> Cryptor? {
250+
private func cryptor(matching header: CryptorHeader) -> (any Cryptor)? {
241251
header.cryptorId() == defaultCryptor.id ? defaultCryptor : cryptors.first(where: {
242252
$0.id == header.cryptorId()
243253
})
@@ -246,7 +256,6 @@ public struct CryptoModule {
246256

247257
/// Convenience methods for creating `CryptoModule`
248258
public extension CryptoModule {
249-
250259
/// Returns **recommended** `CryptoModule` for encryption/decryption
251260
///
252261
/// - Parameters:
@@ -279,7 +288,9 @@ extension CryptoModule: Equatable {
279288

280289
extension CryptoModule: Hashable {
281290
public func hash(into hasher: inout Hasher) {
282-
hasher.combine(cryptors.map { $0.id })
291+
for cryptor in cryptors {
292+
hasher.combine(cryptor)
293+
}
283294
}
284295
}
285296

@@ -289,7 +300,7 @@ extension CryptoModule: CustomStringConvertible {
289300
}
290301
}
291302

292-
internal extension CryptoModule {
303+
extension CryptoModule {
293304
func encrypt(string: String) -> Result<Base64EncodedString, PubNubError> {
294305
guard let data = string.data(using: .utf8) else {
295306
return .failure(PubNubError(
@@ -309,8 +320,8 @@ internal extension CryptoModule {
309320
} else {
310321
return .failure(PubNubError(
311322
.decryptionFailure,
312-
additional: ["Cannot create String from provided Data"])
313-
)
323+
additional: ["Cannot create String from provided Data"]
324+
))
314325
}
315326
}
316327
}

Sources/PubNub/Helpers/Crypto/Cryptors/AESCBCCryptor.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,10 @@ public struct AESCBCCryptor: Cryptor {
147147
}
148148
}
149149
}
150+
151+
extension AESCBCCryptor: Hashable {
152+
public func hash(into hasher: inout Hasher) {
153+
hasher.combine(key)
154+
hasher.combine(id)
155+
}
156+
}

Sources/PubNub/Helpers/Crypto/Cryptors/Cryptor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public struct EncryptedStreamData {
3333
public typealias CryptorId = [UInt8]
3434

3535
/// Protocol for all types that encapsulate concrete encryption/decryption operations
36-
public protocol Cryptor {
36+
public protocol Cryptor: Hashable {
3737
/// Unique 4-byte identifier across all `Cryptor`
3838
///
3939
/// - Important: `[0x41, 0x43, 0x52, 0x48]` and `[0x00, 0x00, 0x00, 0x00]` values are reserved

Sources/PubNub/Helpers/Crypto/Cryptors/LegacyCryptor.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,11 @@ public struct LegacyCryptor: Cryptor {
171171
}
172172
}
173173
}
174+
175+
extension LegacyCryptor: Hashable {
176+
public func hash(into hasher: inout Hasher) {
177+
hasher.combine(key)
178+
hasher.combine(withRandomIV)
179+
hasher.combine(LegacyCryptor.ID)
180+
}
181+
}

Sources/PubNub/PubNub.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,9 +1381,9 @@ public extension PubNub {
13811381
// MARK: - Crypto
13821382

13831383
extension PubNub {
1384-
/// Encrypt some `Data` using the configuration `CryptoModule` value
1384+
/// Encrypts the `Data` object using `CryptoModule` provided in configuration
13851385
/// - Parameter message: The plain text message to be encrypted
1386-
/// - Returns: A `Result` containing either the encryped Data (mapped to Base64-encoded data) or the Crypto Error
1386+
/// - Returns: A `Result` containing either the encryped `Data` (mapped to Base64-encoded data) or the `CryptoError`
13871387
public func encrypt(message: String) -> Result<Data, Error> {
13881388
guard let cryptoModule = configuration.cryptoModule else {
13891389
PubNub.log.error(ErrorDescription.missingCryptoKey)
@@ -1400,9 +1400,9 @@ extension PubNub {
14001400
}
14011401
}
14021402

1403-
/// Decrypt some `Data` using the configuration CryptoModule value
1403+
/// Decrypts the given `Data` object using `CryptoModule` provided in `configuration`
14041404
/// - Parameter message: The encrypted `Data` to decrypt
1405-
/// - Returns: A `Result` containing either the decrypted plain text message or the Crypto Error
1405+
/// - Returns: A `Result` containing either the decrypted plain text message or the `CryptoError`
14061406
public func decrypt(data: Data) -> Result<String, Error> {
14071407
guard let cryptoModule = configuration.cryptoModule else {
14081408
PubNub.log.error(ErrorDescription.missingCryptoKey)

0 commit comments

Comments
 (0)