Skip to content

Commit

Permalink
Fixes for PubNub client (#160)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jguz-pubnub authored Mar 18, 2024
1 parent 8ec1c16 commit c58d58e
Show file tree
Hide file tree
Showing 16 changed files with 318 additions and 161 deletions.
11 changes: 9 additions & 2 deletions .pubnub.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
---
name: swift
scm: github.com/pubnub/swift
version: "7.0.0"
version: "7.1.0"
schema: 1
changelog:
- date: 2024-03-18
version: 7.1.0
changes:
- type: bug
text: "Preventing disconnection when subscribing via `PubNub` to a channel that was already subscribed to."
- type: bug
text: "Fixes for computing a hash value in `CryptoModule`."
- date: 2024-02-21
version: 7.0.0
changes:
Expand Down Expand Up @@ -524,7 +531,7 @@ sdks:
- distribution-type: source
distribution-repository: GitHub release
package-name: PubNub
location: https://github.com/pubnub/swift/archive/refs/tags/7.0.0.zip
location: https://github.com/pubnub/swift/archive/refs/tags/7.1.0.zip
supported-platforms:
supported-operating-systems:
macOS:
Expand Down
16 changes: 8 additions & 8 deletions PubNub.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3843,7 +3843,7 @@
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 7.0.0;
MARKETING_VERSION = 7.1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubUser;
Expand Down Expand Up @@ -3892,7 +3892,7 @@
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 7.0.0;
MARKETING_VERSION = 7.1.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubUser;
Expand Down Expand Up @@ -3998,7 +3998,7 @@
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 7.0.0;
MARKETING_VERSION = 7.1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubSpace;
Expand Down Expand Up @@ -4049,7 +4049,7 @@
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 7.0.0;
MARKETING_VERSION = 7.1.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubSpace;
Expand Down Expand Up @@ -4168,7 +4168,7 @@
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 7.0.0;
MARKETING_VERSION = 7.1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubMembership;
Expand Down Expand Up @@ -4218,7 +4218,7 @@
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 7.0.0;
MARKETING_VERSION = 7.1.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.pubnub.swift.PubNubMembership;
Expand Down Expand Up @@ -4696,7 +4696,7 @@
"$(TOOLCHAIN_DIR)/usr/lib/swift/macosx",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 7.0.0;
MARKETING_VERSION = 7.1.0;
OTHER_CFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited)";
Expand Down Expand Up @@ -4737,7 +4737,7 @@
"$(TOOLCHAIN_DIR)/usr/lib/swift/macosx",
);
MACOSX_DEPLOYMENT_TARGET = 10.13;
MARKETING_VERSION = 7.0.0;
MARKETING_VERSION = 7.1.0;
OTHER_CFLAGS = "$(inherited)";
OTHER_LDFLAGS = "$(inherited)";
OTHER_SWIFT_FLAGS = "$(inherited)";
Expand Down
2 changes: 1 addition & 1 deletion PubNubSwift.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'PubNubSwift'
s.version = '7.0.0'
s.version = '7.1.0'
s.homepage = 'https://github.com/pubnub/swift'
s.documentation_url = 'https://www.pubnub.com/docs/swift-native/pubnub-swift-sdk'
s.authors = { 'PubNub, Inc.' => '[email protected]' }
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Build Status](https://travis-ci.org/pubnub/swift.svg?branch=master)](https://travis-ci.org/pubnub/swift)
[![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)
[![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)

This is the official PubNub Swift SDK repository.

Expand Down Expand Up @@ -59,7 +59,7 @@ end

> Note: Replace `YOUR_TARGET_NAME` with your target's name.
In the directory containing your `Podfile`. execute the following:
In the directory containing your `Podfile` execute the following:

```bash
pod install
Expand Down
2 changes: 1 addition & 1 deletion Sources/PubNub/Helpers/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public enum Constant {

static let pubnubSwiftSDKName: String = "PubNubSwift"

static let pubnubSwiftSDKVersion: String = "7.0.0"
static let pubnubSwiftSDKVersion: String = "7.1.0"

static let appBundleId: String = {
if let info = Bundle.main.infoDictionary,
Expand Down
67 changes: 39 additions & 28 deletions Sources/PubNub/Helpers/Crypto/CryptoModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,29 +15,31 @@ public class CryptorModule {
public static func aesCbcCryptoModule(with key: String, withRandomIV: Bool = true) -> CryptoModule {
preconditionFailure("This method is no longer available")
}

public static func legacyCryptoModule(with key: String, withRandomIV: Bool = true) -> CryptoModule {
preconditionFailure("This method is no longer available")
}
}

/// Object capable of encryption/decryption
public struct CryptoModule {
private let defaultCryptor: Cryptor
private let cryptors: [Cryptor]
private let defaultCryptor: any Cryptor
private let cryptors: [any Cryptor]
private let legacyCryptorId: CryptorId = []

typealias Base64EncodedString = String

/// Initializes `CryptoModule` with custom ``Cryptor`` objects capable of encryption and decryption
///
/// 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`.
/// Otherwise, refer to convenience static factory methods such as ``aesCbcCryptoModule(with:withRandomIV:)``
/// and ``legacyCryptoModule(with:withRandomIV:)`` that return `CryptoModule` configured for you.
/// 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`. Otherwise, refer to convenience static factory methods
/// such as ``aesCbcCryptoModule(with:withRandomIV:)``and ``legacyCryptoModule(with:withRandomIV:)``
/// that return `CryptoModule` configured for you.
///
/// - Parameters:
/// - default: Primary ``Cryptor`` instance used for encryption and decryption
/// - cryptors: An optional list of ``Cryptor`` instances which older messages/files were encoded
public init(default cryptor: Cryptor, cryptors: [Cryptor] = []) {
public init(default cryptor: any Cryptor, cryptors: [any Cryptor] = []) {
self.defaultCryptor = cryptor
self.cryptors = cryptors
}
Expand All @@ -46,13 +48,15 @@ public struct CryptoModule {
///
/// - Parameters:
/// - data: Data to encrypt
/// - Returns: A success, storing encrypted `Data` if operation succeeds. Otherwise, a failure storing `PubNubError` is returned
/// - Returns:
/// - **Success**: An encrypted `Data` object
/// - **Failure**: `PubNubError` describing the reason of failure
public func encrypt(data: Data) -> Result<Data, PubNubError> {
guard !data.isEmpty else {
return .failure(PubNubError(
.encryptionFailure,
additional: ["Cannot encrypt empty Data"])
)
additional: ["Cannot encrypt empty Data"]
))
}
return defaultCryptor.encrypt(data: data).map {
if defaultCryptor.id == LegacyCryptor.ID {
Expand All @@ -71,13 +75,15 @@ public struct CryptoModule {
///
/// - Parameters:
/// - data: Data to decrypt
/// - Returns: A success, storing decrypted `Data` if operation succeeds. Otherwise, a failure storing `PubNubError` is returned
/// - Returns:
/// - **Success**: A decrypted `Data` object
/// - **Failure**: `PubNubError` describing the reason of failure
public func decrypt(data: Data) -> Result<Data, PubNubError> {
guard !data.isEmpty else {
return .failure(PubNubError(
.decryptionFailure,
additional: ["Cannot decrypt empty Data in \(String(describing: self))"])
)
additional: ["Cannot decrypt empty Data in \(String(describing: self))"]
))
}
do {
let header = try CryptorHeader.from(data: data)
Expand All @@ -87,7 +93,7 @@ public struct CryptoModule {
.unknownCryptorFailure,
additional: [
"Could not find matching Cryptor for \(header.cryptorId()) while decrypting Data. " +
"Ensure the corresponding instance is registered in \(String(describing: Self.self))"
"Ensure the corresponding instance is registered in \(String(describing: Self.self))"
]
))
}
Expand Down Expand Up @@ -115,8 +121,8 @@ public struct CryptoModule {
if $0.isEmpty {
return .failure(PubNubError(
.decryptionFailure,
additional: ["Decrypting resulted with empty Data"])
)
additional: ["Decrypting resulted with empty Data"]
))
}
return .success($0)
}
Expand All @@ -129,8 +135,8 @@ public struct CryptoModule {
return .failure(PubNubError(
.decryptionFailure,
underlying: error,
additional: ["Cannot decrypt InputStream"])
)
additional: ["Cannot decrypt InputStream"]
))
}
}

Expand All @@ -139,7 +145,9 @@ public struct CryptoModule {
/// - Parameters:
/// - stream: Stream to encrypt
/// - contentLength: Content length of encoded stream
/// - Returns: A success, storing an `InputStream` value if operation succeeds. Otherwise, a failure storing `PubNubError` is returned
/// - Returns:
/// - **Success**: An `InputStream` value
/// - **Failure**: `PubNubError` describing the reason of failure
public func encrypt(stream: InputStream, contentLength: Int) -> Result<InputStream, PubNubError> {
guard contentLength > 0 else {
return .failure(PubNubError(
Expand Down Expand Up @@ -179,7 +187,9 @@ public struct CryptoModule {
/// - stream: Stream to decrypt
/// - contentLength: Content length of encrypted stream
/// - to: URL where the stream should be decrypted to
/// - Returns: A success, storing a decrypted `InputStream` value if operation succeeds. Otherwise, a failure storing `PubNubError` is returned
/// - Returns:
/// - **Success**: A decrypted `InputStream` object
/// - **Failure**: `PubNubError` describing the reason of failure
@discardableResult
public func decrypt(
stream: InputStream,
Expand All @@ -203,7 +213,7 @@ public struct CryptoModule {
.unknownCryptorFailure,
additional: [
"Could not find matching Cryptor for \(readHeaderResp.header.cryptorId()) while decrypting InputStream. " +
"Ensure the corresponding instance is registered in \(String(describing: Self.self))"
"Ensure the corresponding instance is registered in \(String(describing: Self.self))"
]
))
}
Expand All @@ -218,8 +228,8 @@ public struct CryptoModule {
if outputPath.sizeOf == 0 {
return .failure(PubNubError(
.decryptionFailure,
additional: ["Decrypting resulted with an empty File"])
)
additional: ["Decrypting resulted with an empty File"]
))
}
return .success($0)
}
Expand All @@ -237,7 +247,7 @@ public struct CryptoModule {
}
}

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

/// Convenience methods for creating `CryptoModule`
public extension CryptoModule {

/// Returns **recommended** `CryptoModule` for encryption/decryption
///
/// - Parameters:
Expand Down Expand Up @@ -279,7 +288,9 @@ extension CryptoModule: Equatable {

extension CryptoModule: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(cryptors.map { $0.id })
for cryptor in cryptors {
hasher.combine(cryptor)
}
}
}

Expand All @@ -289,7 +300,7 @@ extension CryptoModule: CustomStringConvertible {
}
}

internal extension CryptoModule {
extension CryptoModule {
func encrypt(string: String) -> Result<Base64EncodedString, PubNubError> {
guard let data = string.data(using: .utf8) else {
return .failure(PubNubError(
Expand All @@ -309,8 +320,8 @@ internal extension CryptoModule {
} else {
return .failure(PubNubError(
.decryptionFailure,
additional: ["Cannot create String from provided Data"])
)
additional: ["Cannot create String from provided Data"]
))
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions Sources/PubNub/Helpers/Crypto/Cryptors/AESCBCCryptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,10 @@ public struct AESCBCCryptor: Cryptor {
}
}
}

extension AESCBCCryptor: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(key)
hasher.combine(id)
}
}
2 changes: 1 addition & 1 deletion Sources/PubNub/Helpers/Crypto/Cryptors/Cryptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public struct EncryptedStreamData {
public typealias CryptorId = [UInt8]

/// Protocol for all types that encapsulate concrete encryption/decryption operations
public protocol Cryptor {
public protocol Cryptor: Hashable {
/// Unique 4-byte identifier across all `Cryptor`
///
/// - Important: `[0x41, 0x43, 0x52, 0x48]` and `[0x00, 0x00, 0x00, 0x00]` values are reserved
Expand Down
8 changes: 8 additions & 0 deletions Sources/PubNub/Helpers/Crypto/Cryptors/LegacyCryptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,11 @@ public struct LegacyCryptor: Cryptor {
}
}
}

extension LegacyCryptor: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(key)
hasher.combine(withRandomIV)
hasher.combine(LegacyCryptor.ID)
}
}
8 changes: 4 additions & 4 deletions Sources/PubNub/PubNub.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1381,9 +1381,9 @@ public extension PubNub {
// MARK: - Crypto

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

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

0 comments on commit c58d58e

Please sign in to comment.