diff --git a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift index 0b1512247..25556f9b3 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift @@ -15,8 +15,8 @@ public final class OpenGroupAPI: NSObject { // MARK: - Polling State - private static var hasPerformedInitialPoll: AtomicDict = AtomicDict() - private static var timeSinceLastPoll: AtomicDict = AtomicDict() + private static var hasPerformedInitialPoll: Atomic<[String: Bool]> = Atomic([:]) + private static var timeSinceLastPoll: Atomic<[String: TimeInterval]> = Atomic([:]) private static var lastPollTime: Atomic = Atomic(.greatestFiniteMagnitude) private static let timeSinceLastOpen: Atomic = { @@ -27,7 +27,7 @@ public final class OpenGroupAPI: NSObject { // TODO: Remove these - private static var legacyAuthTokenPromises: AtomicDict> = AtomicDict() + private static var legacyAuthTokenPromises: Atomic<[String: Promise]> = Atomic([:]) private static var legacyHasUpdatedLastOpenDate = false private static var legacyGroupImagePromises: [String: Promise] = [:] @@ -43,14 +43,14 @@ public final class OpenGroupAPI: NSObject { /// - Inbox for the server public static func poll(_ server: String, using dependencies: Dependencies = Dependencies()) -> Promise<[Endpoint: (OnionRequestResponseInfoType, Codable?)]> { // Store a local copy of the cached state for this server - let hadPerformedInitialPoll: Bool = (hasPerformedInitialPoll[server] == true) - let originalTimeSinceLastPoll: TimeInterval = (timeSinceLastPoll[server] ?? min(lastPollTime.wrappedValue, timeSinceLastOpen.wrappedValue)) + let hadPerformedInitialPoll: Bool = (hasPerformedInitialPoll.wrappedValue[server] == true) + let originalTimeSinceLastPoll: TimeInterval = (timeSinceLastPoll.wrappedValue[server] ?? min(lastPollTime.wrappedValue, timeSinceLastOpen.wrappedValue)) let maybeLastInboxMessageId: Int64? = dependencies.storage.getOpenGroupInboxLatestMessageId(for: server) let lastInboxMessageId: Int64 = (maybeLastInboxMessageId ?? 0) // Update the cached state for this server - hasPerformedInitialPoll.wrappedValue[server] = true - lastPollTime.wrappedValue = min(lastPollTime.wrappedValue, timeSinceLastOpen.wrappedValue) + hasPerformedInitialPoll.mutate { $0[server] = true } + lastPollTime.mutate { $0 = min($0, timeSinceLastOpen.wrappedValue)} UserDefaults.standard[.lastOpen] = Date() // Generate the requests @@ -58,13 +58,13 @@ public final class OpenGroupAPI: NSObject { BatchRequestInfo( request: Request( server: server, - endpoint: .capabilities, - queryParameters: [:] // TODO: Add any requirements '.required' + endpoint: .capabilities ), responseType: Capabilities.self ) ] .appending( + // Per-room requests dependencies.storage.getAllOpenGroups().values .filter { $0.server == server.lowercased() } // Note: The `OpenGroup` type converts to lowercase in init .flatMap { openGroup -> [BatchRequestInfoType] in diff --git a/SessionMessagingKit/Open Groups/Types/Dependencies.swift b/SessionMessagingKit/Open Groups/Types/Dependencies.swift index b4e011d9c..fd64511b8 100644 --- a/SessionMessagingKit/Open Groups/Types/Dependencies.swift +++ b/SessionMessagingKit/Open Groups/Types/Dependencies.swift @@ -4,42 +4,94 @@ import Foundation import Sodium import SessionSnodeKit +// MARK: - Dependencies + extension OpenGroupAPI { - public struct Dependencies { - let api: OnionRequestAPIType.Type - let storage: SessionMessagingKitStorageProtocol - let sodium: SodiumType - let aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType - let sign: SignType - let genericHash: GenericHashType - let ed25519: Ed25519Type.Type - let nonceGenerator16: NonceGenerator16ByteType - let nonceGenerator24: NonceGenerator24ByteType - let date: Date + public class Dependencies { + private var _api: OnionRequestAPIType.Type? + var api: OnionRequestAPIType.Type { + get { getValueSettingIfNull(&_api) { OnionRequestAPI.self } } + set { _api = newValue } + } + + private var _storage: SessionMessagingKitStorageProtocol? + var storage: SessionMessagingKitStorageProtocol { + get { getValueSettingIfNull(&_storage) { SNMessagingKitConfiguration.shared.storage } } + set { _storage = newValue } + } + + private var _sodium: SodiumType? + var sodium: SodiumType { + get { getValueSettingIfNull(&_sodium) { Sodium() } } + set { _sodium = newValue } + } + + private var _aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? + var aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType { + get { getValueSettingIfNull(&_aeadXChaCha20Poly1305Ietf) { sodium.getAeadXChaCha20Poly1305Ietf() } } + set { _aeadXChaCha20Poly1305Ietf = newValue } + } + + private var _sign: SignType? + var sign: SignType { + get { getValueSettingIfNull(&_sign) { sodium.getSign() } } + set { _sign = newValue } + } + + private var _genericHash: GenericHashType? + var genericHash: GenericHashType { + get { getValueSettingIfNull(&_genericHash) { sodium.getGenericHash() } } + set { _genericHash = newValue } + } + + private var _ed25519: Ed25519Type.Type? + var ed25519: Ed25519Type.Type { + get { getValueSettingIfNull(&_ed25519) { Ed25519.self } } + set { _ed25519 = newValue } + } + + private var _nonceGenerator16: NonceGenerator16ByteType? + var nonceGenerator16: NonceGenerator16ByteType { + get { getValueSettingIfNull(&_nonceGenerator16) { NonceGenerator16Byte() } } + set { _nonceGenerator16 = newValue } + } + + private var _nonceGenerator24: NonceGenerator24ByteType? + var nonceGenerator24: NonceGenerator24ByteType { + get { getValueSettingIfNull(&_nonceGenerator24) { NonceGenerator24Byte() } } + set { _nonceGenerator24 = newValue } + } + + private var _date: Date? + var date: Date { + get { getValueSettingIfNull(&_date) { Date() } } + set { _date = newValue } + } + + // MARK: - Initialization public init( - api: OnionRequestAPIType.Type = OnionRequestAPI.self, - storage: SessionMessagingKitStorageProtocol = SNMessagingKitConfiguration.shared.storage, - // TODO: Shift the next 3 to be abstracted behind a single "signing" class? - sodium: SodiumType = Sodium(), + api: OnionRequestAPIType.Type? = nil, + storage: SessionMessagingKitStorageProtocol? = nil, + sodium: SodiumType? = nil, aeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType? = nil, sign: SignType? = nil, genericHash: GenericHashType? = nil, - ed25519: Ed25519Type.Type = Ed25519.self, - nonceGenerator16: NonceGenerator16ByteType = NonceGenerator16Byte(), - nonceGenerator24: NonceGenerator24ByteType = NonceGenerator24Byte(), - date: Date = Date() + ed25519: Ed25519Type.Type? = nil, + nonceGenerator16: NonceGenerator16ByteType? = nil, + nonceGenerator24: NonceGenerator24ByteType? = nil, + date: Date? = nil ) { - self.api = api - self.storage = storage - self.sodium = sodium - self.aeadXChaCha20Poly1305Ietf = (aeadXChaCha20Poly1305Ietf ?? sodium.getAeadXChaCha20Poly1305Ietf()) - self.sign = (sign ?? sodium.getSign()) - self.genericHash = (genericHash ?? sodium.getGenericHash()) - self.ed25519 = ed25519 - self.nonceGenerator16 = nonceGenerator16 - self.nonceGenerator24 = nonceGenerator24 - self.date = date + _api = api + _storage = storage + _sodium = sodium + _aeadXChaCha20Poly1305Ietf = aeadXChaCha20Poly1305Ietf + _sign = sign + _genericHash = genericHash + _ed25519 = ed25519 + _nonceGenerator16 = nonceGenerator16 + _nonceGenerator24 = nonceGenerator24 + _date = date } // MARK: - Convenience @@ -57,17 +109,29 @@ extension OpenGroupAPI { date: Date? = nil ) -> Dependencies { return Dependencies( - api: (api ?? self.api), - storage: (storage ?? self.storage), - sodium: (sodium ?? self.sodium), - aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self.aeadXChaCha20Poly1305Ietf), - sign: (sign ?? self.sign), - genericHash: (genericHash ?? self.genericHash), - ed25519: (ed25519 ?? self.ed25519), - nonceGenerator16: (nonceGenerator16 ?? self.nonceGenerator16), - nonceGenerator24: (nonceGenerator24 ?? self.nonceGenerator24), - date: (date ?? self.date) + api: (api ?? self._api), + storage: (storage ?? self._storage), + sodium: (sodium ?? self._sodium), + aeadXChaCha20Poly1305Ietf: (aeadXChaCha20Poly1305Ietf ?? self._aeadXChaCha20Poly1305Ietf), + sign: (sign ?? self._sign), + genericHash: (genericHash ?? self._genericHash), + ed25519: (ed25519 ?? self._ed25519), + nonceGenerator16: (nonceGenerator16 ?? self._nonceGenerator16), + nonceGenerator24: (nonceGenerator24 ?? self._nonceGenerator24), + date: (date ?? self._date) ) } } } + +// MARK: - Convenience + +fileprivate func getValueSettingIfNull(_ maybeValue: inout T?, _ valueGenerator: () -> T) -> T { + guard let value: T = maybeValue else { + let value: T = valueGenerator() + maybeValue = value + return value + } + + return value +} diff --git a/SessionMessagingKit/Utilities/Atomic.swift b/SessionMessagingKit/Utilities/Atomic.swift index 69d5ecf7c..7d8b07d95 100644 --- a/SessionMessagingKit/Utilities/Atomic.swift +++ b/SessionMessagingKit/Utilities/Atomic.swift @@ -2,94 +2,45 @@ import Foundation -/// See https://www.donnywals.com/why-your-atomic-property-wrapper-doesnt-work-for-collection-types/ -/// for more information about the below types - -protocol UnsupportedType {} - -extension Array: UnsupportedType {} -extension Set: UnsupportedType {} -extension Dictionary: UnsupportedType {} - // MARK: - Atomic /// The `Atomic` wrapper is a generic wrapper providing a thread-safe way to get and set a value -@propertyWrapper -struct Atomic { - private let queue: DispatchQueue = DispatchQueue(label: "io.oxen.\(UUID().uuidString)", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .inherit, target: .global()) - private var value: Value - - init(_ initialValue: Value) { - if initialValue is UnsupportedType { preconditionFailure("Use the appropriate Aromic... type for collections") } - - self.value = initialValue - } - - var wrappedValue: Value { - get { return queue.sync { return value } } - set { return queue.sync { value = newValue } } - } -} - -extension Atomic where Value: CustomDebugStringConvertible { - var debugDescription: String { - return value.debugDescription - } -} - -// MARK: - AtomicArray - -/// The `AtomicArray` wrapper is a generic wrapper providing a thread-safe way to get and set an array or one of it's values /// -/// Note: This is a class rather than a struct as you need to modify a reference rather than a copy for the concurrency to work +/// A write-up on the need for this class and it's approach can be found here: +/// https://www.vadimbulavin.com/swift-atomic-properties-with-property-wrappers/ +/// there is also another approach which can be taken but it requires separate types for collections and results in +/// a somewhat inconsistent interface between different `Atomic` wrappers @propertyWrapper -class AtomicArray: CustomDebugStringConvertible { - private let queue: DispatchQueue = DispatchQueue(label: "io.oxen.\(UUID().uuidString)", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .inherit, target: .global()) - private var value: [Value] - - init(_ initialValue: [Value] = []) { - self.value = initialValue - } - - var wrappedValue: [Value] { - get { return queue.sync { return value } } - set { return queue.sync { value = newValue } } - } +public class Atomic { + private let queue: DispatchQueue = DispatchQueue(label: "io.oxen.\(UUID().uuidString)") + private var value: Value - subscript(index: Int) -> Value { - get { queue.sync { value[index] }} - set { queue.async(flags: .barrier) { self.value[index] = newValue } } + /// In order to change the value you **must** use the `mutate` function + public var wrappedValue: Value { + return queue.sync { return value } } - public var debugDescription: String { - return value.debugDescription + /// For more information see https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#projections + public var projectedValue: Atomic { + return self } -} - -// MARK: - AtomicDict - -/// The `AtomicDict` wrapper is a generic wrapper providing a thread-safe way to get and set a dictionaries or one of it's values -/// -/// Note: This is a class rather than a struct as you need to modify a reference rather than a copy for the concurrency to work -@propertyWrapper -class AtomicDict: CustomDebugStringConvertible { - private let queue: DispatchQueue = DispatchQueue(label: "io.oxen.\(UUID().uuidString)", qos: .utility, attributes: .concurrent, autoreleaseFrequency: .inherit, target: .global()) - private var value: [Key: Value] + + // MARK: - Initialization - init(_ initialValue: [Key: Value] = [:]) { + init(_ initialValue: Value) { self.value = initialValue } - var wrappedValue: [Key: Value] { - get { return queue.sync { return value } } - set { return queue.sync { value = newValue } } - } + // MARK: - Functions - subscript(key: Key) -> Value? { - get { queue.sync { value[key] }} - set { queue.async(flags: .barrier) { self.value[key] = newValue } } + func mutate(_ mutation: (inout Value) -> Void) { + return queue.sync { + mutation(&value) + } } - +} + +extension Atomic where Value: CustomDebugStringConvertible { var debugDescription: String { return value.debugDescription } diff --git a/SessionMessagingKitTests/Open Groups/OpenGroupAPIV2Tests.swift b/SessionMessagingKitTests/Open Groups/OpenGroupAPIV2Tests.swift index ceae8ac77..566769417 100644 --- a/SessionMessagingKitTests/Open Groups/OpenGroupAPIV2Tests.swift +++ b/SessionMessagingKitTests/Open Groups/OpenGroupAPIV2Tests.swift @@ -21,9 +21,17 @@ class OpenGroupAPITests: XCTestCase { } } - struct TestNonceGenerator: NonceGenerator16ByteType { + struct TestNonce16Generator: NonceGenerator16ByteType { + var NonceBytes: Int = 16 + func nonce() -> Array { return Data(base64Encoded: "pK6YRtQApl4NhECGizF0Cg==")!.bytes } } + + struct TestNonce24Generator: NonceGenerator24ByteType { + var NonceBytes: Int = 24 + + func nonce() -> Array { return Data(base64Encoded: "pbTUizreT0sqJ2R2LloseQDyVL2RYztD")!.bytes } + } class TestApi: OnionRequestAPIType { struct RequestData: Codable { @@ -89,7 +97,8 @@ class OpenGroupAPITests: XCTestCase { sign: testSign, genericHash: testGenericHash, ed25519: TestEd25519.self, - nonceGenerator: TestNonceGenerator(), + nonceGenerator16: TestNonce16Generator(), + nonceGenerator24: TestNonce24Generator(), date: Date(timeIntervalSince1970: 1234567890) ) @@ -142,21 +151,35 @@ class OpenGroupAPITests: XCTestCase { OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], - body: OpenGroupAPI.Capabilities(capabilities: [], missing: nil) + body: OpenGroupAPI.Capabilities(capabilities: [], missing: nil), + failedToParseBody: false ) ), try! JSONEncoder().encode( OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], - body: try! JSONDecoder().decode(OpenGroupAPI.RoomPollInfo.self, from: "{}".data(using: .utf8)!) + body: try! JSONDecoder().decode( + OpenGroupAPI.RoomPollInfo.self, + from: """ + { + \"token\":\"test\", + \"active_users\":1, + \"read\":true, + \"write\":true, + \"upload\":true + } + """.data(using: .utf8)! + ), + failedToParseBody: false ) ), try! JSONEncoder().encode( OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], - body: [OpenGroupAPI.Message]() + body: [OpenGroupAPI.Message](), + failedToParseBody: false ) ) ] @@ -166,7 +189,7 @@ class OpenGroupAPITests: XCTestCase { } dependencies = dependencies.with(api: LocalTestApi.self) - var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil + var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil var error: Error? = nil OpenGroupAPI.poll("testServer", using: dependencies) @@ -197,7 +220,7 @@ class OpenGroupAPITests: XCTestCase { } func testPollReturnsAnErrorWhenGivenNoData() throws { - var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil + var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil var error: Error? = nil OpenGroupAPI.poll("testServer", using: dependencies) @@ -220,7 +243,7 @@ class OpenGroupAPITests: XCTestCase { } dependencies = dependencies.with(api: LocalTestApi.self) - var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil + var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil var error: Error? = nil OpenGroupAPI.poll("testServer", using: dependencies) @@ -243,7 +266,7 @@ class OpenGroupAPITests: XCTestCase { } dependencies = dependencies.with(api: LocalTestApi.self) - var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil + var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil var error: Error? = nil OpenGroupAPI.poll("testServer", using: dependencies) @@ -266,7 +289,7 @@ class OpenGroupAPITests: XCTestCase { } dependencies = dependencies.with(api: LocalTestApi.self) - var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil + var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil var error: Error? = nil OpenGroupAPI.poll("testServer", using: dependencies) @@ -291,14 +314,27 @@ class OpenGroupAPITests: XCTestCase { OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], - body: OpenGroupAPI.Capabilities(capabilities: [], missing: nil) + body: OpenGroupAPI.Capabilities(capabilities: [], missing: nil), + failedToParseBody: false ) ), try! JSONEncoder().encode( OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], - body: try! JSONDecoder().decode(OpenGroupAPI.RoomPollInfo.self, from: "{}".data(using: .utf8)!) + body: try! JSONDecoder().decode( + OpenGroupAPI.RoomPollInfo.self, + from: """ + { + \"token\":\"test\", + \"active_users\":1, + \"read\":true, + \"write\":true, + \"upload\":true + } + """.data(using: .utf8)! + ), + failedToParseBody: false ) ) ] @@ -308,7 +344,7 @@ class OpenGroupAPITests: XCTestCase { } dependencies = dependencies.with(api: LocalTestApi.self) - var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil + var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil var error: Error? = nil OpenGroupAPI.poll("testServer", using: dependencies) @@ -333,21 +369,24 @@ class OpenGroupAPITests: XCTestCase { OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], - body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: "") + body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: ""), + failedToParseBody: false ) ), try! JSONEncoder().encode( OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], - body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: "") + body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: ""), + failedToParseBody: false ) ), try! JSONEncoder().encode( OpenGroupAPI.BatchSubResponse( code: 200, headers: [:], - body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: "") + body: OpenGroupAPI.PinnedMessage(id: 1, pinnedAt: 1, pinnedBy: ""), + failedToParseBody: false ) ) ] @@ -357,7 +396,7 @@ class OpenGroupAPITests: XCTestCase { } dependencies = dependencies.with(api: LocalTestApi.self) - var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable)]? = nil + var response: [OpenGroupAPI.Endpoint: (OnionRequestResponseInfoType, Codable?)]? = nil var error: Error? = nil OpenGroupAPI.poll("testServer", using: dependencies) @@ -566,6 +605,7 @@ class OpenGroupAPITests: XCTestCase { func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return Sodium().aead.xchacha20poly1305ietf } func getSign() -> SignType { return Sodium().sign } + func generateBlindingFactor(serverPublicKey: String) -> Bytes? { return nil } func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? { return nil } @@ -573,7 +613,14 @@ class OpenGroupAPITests: XCTestCase { return nil } - func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? { return nil } + func combineKeys(lhsKeyBytes: Bytes, rhsKeyBytes: Bytes) -> Bytes? { return nil } + func sharedBlindedEncryptionKey(secretKey a: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes? { + return nil + } + + func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool { + return false + } } testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server( name: "testServer", @@ -604,6 +651,7 @@ class OpenGroupAPITests: XCTestCase { func getAeadXChaCha20Poly1305Ietf() -> AeadXChaCha20Poly1305IetfType { return Sodium().aead.xchacha20poly1305ietf } func getSign() -> SignType { return Sodium().sign } + func generateBlindingFactor(serverPublicKey: String) -> Bytes? { return nil } func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? { return Box.KeyPair( publicKey: Data.data(fromHex: "7aecdcade88d881d2327ab011afd2e04c2ec6acffc9e9df45aaf78a151bd2f7d")!.bytes, @@ -614,7 +662,14 @@ class OpenGroupAPITests: XCTestCase { return nil } - func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? { return nil } + func combineKeys(lhsKeyBytes: Bytes, rhsKeyBytes: Bytes) -> Bytes? { return nil } + func sharedBlindedEncryptionKey(secretKey a: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes? { + return nil + } + + func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool { + return false + } } testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server( name: "testServer", @@ -641,8 +696,11 @@ class OpenGroupAPITests: XCTestCase { func testItFailsToSignIfUnblindedAndTheSignatureDoesNotGetGenerated() throws { class InvalidSign: SignType { + var PublicKeyBytes: Int = 32 + func signature(message: Bytes, secretKey: Bytes) -> Bytes? { return nil } func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool { return false } + func toX25519(ed25519PublicKey: Bytes) -> Bytes? { return nil } } testStorage.mockData[.openGroupServer] = OpenGroupAPI.Server( name: "testServer", diff --git a/SessionMessagingKitTests/_TestUtilities/TestAeadXChaCha20Poly1305Ietf.swift b/SessionMessagingKitTests/_TestUtilities/TestAeadXChaCha20Poly1305Ietf.swift index 0906b8e25..13bffd852 100644 --- a/SessionMessagingKitTests/_TestUtilities/TestAeadXChaCha20Poly1305Ietf.swift +++ b/SessionMessagingKitTests/_TestUtilities/TestAeadXChaCha20Poly1305Ietf.swift @@ -7,10 +7,14 @@ import Sodium @testable import SessionMessagingKit class TestAeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType, Mockable { + var KeyBytes: Int = 32 + var ABytes: Int = 16 + // MARK: - Mockable enum DataKey: Hashable { case encrypt + case decrypt } typealias Key = DataKey @@ -19,7 +23,11 @@ class TestAeadXChaCha20Poly1305Ietf: AeadXChaCha20Poly1305IetfType, Mockable { // MARK: - SignType - func encrypt(message: Bytes, secretKey: Aead.XChaCha20Poly1305Ietf.Key, additionalData: Bytes?) -> (authenticatedCipherText: Bytes, nonce: Aead.XChaCha20Poly1305Ietf.Nonce)? { - return (mockData[.encrypt] as? (authenticatedCipherText: Bytes, nonce: Aead.XChaCha20Poly1305Ietf.Nonce)) + func encrypt(message: Bytes, secretKey: Bytes, nonce: Bytes, additionalData: Bytes?) -> Bytes? { + return (mockData[.encrypt] as? Bytes) + } + + func decrypt(authenticatedCipherText: Bytes, secretKey: Bytes, nonce: Bytes, additionalData: Bytes?) -> Bytes? { + return (mockData[.decrypt] as? Bytes) } } diff --git a/SessionMessagingKitTests/_TestUtilities/TestSign.swift b/SessionMessagingKitTests/_TestUtilities/TestSign.swift index a193b2a5f..3d2402bbf 100644 --- a/SessionMessagingKitTests/_TestUtilities/TestSign.swift +++ b/SessionMessagingKitTests/_TestUtilities/TestSign.swift @@ -7,11 +7,14 @@ import Sodium @testable import SessionMessagingKit class TestSign: SignType, Mockable { + var PublicKeyBytes: Int = 32 + // MARK: - Mockable enum DataKey: Hashable { case signature case verify + case toX25519 } typealias Key = DataKey @@ -27,4 +30,8 @@ class TestSign: SignType, Mockable { func verify(message: Bytes, publicKey: Bytes, signature: Bytes) -> Bool { return (mockData[.verify] as! Bool) } + + func toX25519(ed25519PublicKey: Bytes) -> Bytes? { + return (mockData[.toX25519] as? Bytes) + } } diff --git a/SessionMessagingKitTests/_TestUtilities/TestSodium.swift b/SessionMessagingKitTests/_TestUtilities/TestSodium.swift index 862cbfc8d..b8132ba62 100644 --- a/SessionMessagingKitTests/_TestUtilities/TestSodium.swift +++ b/SessionMessagingKitTests/_TestUtilities/TestSodium.swift @@ -13,9 +13,12 @@ class TestSodium: SodiumType, Mockable { case genericHash case aeadXChaCha20Poly1305Ietf case sign + case blindingFactor case blindedKeyPair case sogsSignature - case sharedEdSecret + case combinedKeys + case sharedBlindedEncryptionKey + case sessionIdMatches } typealias Key = DataKey @@ -32,6 +35,8 @@ class TestSodium: SodiumType, Mockable { func getSign() -> SignType { return (mockData[.sign] as! SignType) } + func generateBlindingFactor(serverPublicKey: String) -> Bytes? { return (mockData[.blindingFactor] as? Bytes) } + func blindedKeyPair(serverPublicKey: String, edKeyPair: Box.KeyPair, genericHash: GenericHashType) -> Box.KeyPair? { return (mockData[.blindedKeyPair] as? Box.KeyPair) } @@ -40,7 +45,15 @@ class TestSodium: SodiumType, Mockable { return (mockData[.sogsSignature] as? Bytes) } - func sharedEdSecret(_ firstKeyBytes: [UInt8], _ secondKeyBytes: [UInt8]) -> Bytes? { - return (mockData[.sharedEdSecret] as? Bytes) + func combineKeys(lhsKeyBytes: Bytes, rhsKeyBytes: Bytes) -> Bytes? { + return (mockData[.combinedKeys] as? Bytes) + } + + func sharedBlindedEncryptionKey(secretKey a: Bytes, otherBlindedPublicKey: Bytes, fromBlindedPublicKey kA: Bytes, toBlindedPublicKey kB: Bytes, genericHash: GenericHashType) -> Bytes? { + return (mockData[.sharedBlindedEncryptionKey] as? Bytes) + } + + func sessionId(_ sessionId: String, matchesBlindedId blindedSessionId: String, serverPublicKey: String) -> Bool { + return ((mockData[.sessionIdMatches] as? Bool) ?? false) } } diff --git a/SessionMessagingKitTests/_TestUtilities/TestStorage.swift b/SessionMessagingKitTests/_TestUtilities/TestStorage.swift index 535a1a43f..494cf03c2 100644 --- a/SessionMessagingKitTests/_TestUtilities/TestStorage.swift +++ b/SessionMessagingKitTests/_TestUtilities/TestStorage.swift @@ -18,6 +18,8 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable { case openGroupServer case openGroupImage case openGroupUserCount + case openGroupSequenceNumber + case openGroupLatestMessageId } typealias Key = DataKey @@ -47,6 +49,7 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable { func getUserED25519KeyPair() -> Box.KeyPair? { return (mockData[.userEdKeyPair] as? Box.KeyPair) } func getUser() -> Contact? { return nil } func getAllContacts() -> Set { return Set() } + func getAllContacts(with transaction: YapDatabaseReadTransaction) -> Set { return Set() } // MARK: - Closed Groups @@ -66,12 +69,6 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable { func resumeMessageSendJobIfNeeded(_ messageSendJobID: String) {} func isJobCanceled(_ job: Job) -> Bool { return true } - // MARK: - Authorization - - func getAuthToken(for room: String, on server: String) -> String? { return nil } - func setAuthToken(for room: String, on server: String, to newValue: String, using transaction: Any) {} - func removeAuthToken(for room: String, on server: String, using transaction: Any) {} - // MARK: - Open Groups func getAllOpenGroups() -> [String: OpenGroup] { return (mockData[.allOpenGroups] as! [String: OpenGroup]) } @@ -96,6 +93,40 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable { mockData[.openGroupUserCount] = newValue } + func getOpenGroupSequenceNumber(for room: String, on server: String) -> Int64? { + let data: [String: Int64] = ((mockData[.openGroupSequenceNumber] as? [String: Int64]) ?? [:]) + return data["\(server).\(room)"] + } + + func setOpenGroupSequenceNumber(for room: String, on server: String, to newValue: Int64, using transaction: Any) { + var updatedData: [String: Int64] = ((mockData[.openGroupSequenceNumber] as? [String: Int64]) ?? [:]) + updatedData["\(server).\(room)"] = newValue + mockData[.openGroupSequenceNumber] = updatedData + } + + func removeOpenGroupSequenceNumber(for room: String, on server: String, using transaction: Any) { + var updatedData: [String: Int64] = ((mockData[.openGroupSequenceNumber] as? [String: Int64]) ?? [:]) + updatedData["\(server).\(room)"] = nil + mockData[.openGroupSequenceNumber] = updatedData + } + + func getOpenGroupInboxLatestMessageId(for server: String) -> Int64? { + let data: [String: Int64] = ((mockData[.openGroupLatestMessageId] as? [String: Int64]) ?? [:]) + return data[server] + } + + func setOpenGroupInboxLatestMessageId(for server: String, to newValue: Int64, using transaction: Any) { + var updatedData: [String: Int64] = ((mockData[.openGroupLatestMessageId] as? [String: Int64]) ?? [:]) + updatedData[server] = newValue + mockData[.openGroupLatestMessageId] = updatedData + } + + func removeOpenGroupInboxLatestMessageId(for server: String, using transaction: Any) { + var updatedData: [String: Int64] = ((mockData[.openGroupLatestMessageId] as? [String: Int64]) ?? [:]) + updatedData[server] = nil + mockData[.openGroupLatestMessageId] = updatedData + } + // MARK: - Open Group Public Keys func getOpenGroupPublicKey(for server: String) -> String? { @@ -108,19 +139,10 @@ class TestStorage: SessionMessagingKitStorageProtocol, Mockable { func setOpenGroupPublicKey(for server: String, to newValue: String, using transaction: Any) {} - // MARK: - Last Message Server ID - - func getLastMessageServerID(for room: String, on server: String) -> Int64? { return nil } - func setLastMessageServerID(for room: String, on server: String, to newValue: Int64, using transaction: Any) {} - func removeLastMessageServerID(for room: String, on server: String, using transaction: Any) {} - - // MARK: - Last Deletion Server ID - - func getLastDeletionServerID(for room: String, on server: String) -> Int64? { return nil } - func setLastDeletionServerID(for room: String, on server: String, to newValue: Int64, using transaction: Any) {} - func removeLastDeletionServerID(for room: String, on server: String, using transaction: Any) {} - // MARK: - Message Handling + + func getAllMessageRequestThreads() -> [String: TSContactThread] { return [:] } + func getAllMessageRequestThreads(using transaction: YapDatabaseReadTransaction) -> [String: TSContactThread] { return [:] } func getReceivedMessageTimestamps(using transaction: Any) -> [UInt64] { return [] } func addReceivedMessageTimestamp(_ timestamp: UInt64, using transaction: Any) {}